<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-tw">
    <title>kanru&#x27;s blog</title>
    <subtitle>kanru&#x27;s blog</subtitle>
    <link rel="self" type="application/atom+xml" href="https://kanru.info/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://kanru.info/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-22T00:00:00+00:00</updated>
    <id>https://kanru.info/atom.xml</id>
    <entry xml:lang="en-us">
        <title>Scoped Error in Rust</title>
        <published>2026-05-22T00:00:00+00:00</published>
        <updated>2026-05-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/scoped-error/"/>
        <id>https://kanru.info/scoped-error/</id>
        
        <content type="html" xml:base="https://kanru.info/scoped-error/">&lt;p&gt;I’ve never been fully satisfied with any error handling crate in Rust. I’ve
tried many and even developed a few helpers. Here are the key issues I
found with each. Theses are issues my &lt;code&gt;scoped-error&lt;&#x2F;code&gt; crate tries to address.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-inspiration&quot;&gt;The Inspiration&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;anyhow&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - good for a drop-in Error type that just works, but requires
adding &lt;code&gt;.with_context()&lt;&#x2F;code&gt; everywhere. It’s verbose and repetitive. Error
reporting requires knowing how &lt;code&gt;anyhow::Error&lt;&#x2F;code&gt; handles format strings. Error
propagation lacks location information; the alternative is backtrace,
which pulls in heavy std dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;thiserror&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - good for defining custom Error types. The &lt;code&gt;#[from]&lt;&#x2F;code&gt; implementation
encourages a single Error type that encompasses all possible sources. But
the ergonomics stop there. Using these types is still tedious if you want
per-module errors with good context. The improvement over manually rolling
Error types seems small compared to the &lt;code&gt;syn&lt;&#x2F;code&gt; and compile-time overhead.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;snafu&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - combines manual context attachment with &lt;code&gt;anyhow&lt;&#x2F;code&gt; and &lt;code&gt;thiserror&lt;&#x2F;code&gt;
patterns in one crate. However, I feel like I’m encoding all my error
branches into &lt;code&gt;Snafu&lt;&#x2F;code&gt; contexts. Those implementation details don’t need
to be public, yet &lt;code&gt;snafu&lt;&#x2F;code&gt; tightly couples the Error type to them. Maybe
I’m using it wrong.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;exn&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - a refreshing approach to error handling. I
actually started my crate based on the pattern from
the blog post &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fast.github.io&#x2F;blog&#x2F;stop-forwarding-errors-start-designing-them&#x2F;&quot;&gt;Stop Forwarding Errors, Start Designing
Them&lt;&#x2F;a&gt;.
The minor issues with &lt;code&gt;exn&lt;&#x2F;code&gt; 0.3 are: (1) you still need to remember
&lt;code&gt;.or_raise(err)&lt;&#x2F;code&gt; for each fallible operation, and it’s easy to miss
for intra-module method calls; (2) the &lt;code&gt;Exn&lt;&#x2F;code&gt; wrapper itself is not a
std &lt;code&gt;Error&lt;&#x2F;code&gt;, so interop with other error types requires adapters like
&lt;code&gt;exn-anyhow&lt;&#x2F;code&gt; or &lt;code&gt;exn-stderr&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While switching between these error crates, I kept noticing a gap: with
&lt;code&gt;anyhow&lt;&#x2F;code&gt;-like crates, you attach context at each call site, but the method
itself lacks it.&lt;&#x2F;p&gt;
&lt;p&gt;Example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; anyhow&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; read_config&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; raw&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;read_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;config.toml&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;raw&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; complex_method&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; read_config&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;validate config file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;cfg&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;parse config file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  Ok&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&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It’s easy to &lt;code&gt;?&lt;&#x2F;code&gt; away the context when all your methods return &lt;code&gt;anyhow::Result&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With &lt;code&gt;exn&lt;&#x2F;code&gt;-like crates, you define error context for each method and attach
it to all error branches. &lt;code&gt;snafu&lt;&#x2F;code&gt; works similarly.&lt;&#x2F;p&gt;
&lt;p&gt;Example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; exn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; ResultExt&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; thiserror&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Debug&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;MyError: &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; MyError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; read_config&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; MyError&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; err&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; MyError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;read config file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; raw&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;read_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;config.toml&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;or_raise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;raw&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; complex_method&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&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: light-dark(#6F42C1, #B392F0);&quot;&gt; MyError&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; err&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; MyError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;complex method&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; read_config&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;or_raise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;cfg&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;or_raise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  Ok&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&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The common issue: it’s easy to &lt;code&gt;?&lt;&#x2F;code&gt; away the context when all methods share the same &lt;code&gt;Result&lt;&#x2F;code&gt; type.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enter-scoped-error&quot;&gt;Enter Scoped Error&lt;&#x2F;h2&gt;
&lt;p&gt;I really liked &lt;code&gt;exn&lt;&#x2F;code&gt;’s approach: define an error closure to force conversion
to the module-scoped Error type. But the repeated &lt;code&gt;.or_raise(err)?&lt;&#x2F;code&gt; gets
annoying fast.&lt;&#x2F;p&gt;
&lt;p&gt;I started creating wrappers to mediate conversion from source errors to
module-scoped Errors. I soon realized this pattern solves several ergonomic
issues with other approaches, and checks the boxes I cared about.&lt;&#x2F;p&gt;
&lt;p&gt;Example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; scoped_error&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; expect_error&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; read_config&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  expect_error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;read config file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; raw&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;read_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;config.toml&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;raw&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; complex_method&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&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: light-dark(#6F42C1, #B392F0);&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;  expect_error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;failed to do complex thing&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; read_config&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;cfg&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Ok&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&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The core idea is simple: attach context exactly once. Not at every call
site, not at every failure point, just between caller and logic. I
want per-module Error types without manual conversion at every step.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;expect_error()&lt;&#x2F;code&gt; has three responsibilities: prepare context for future
errors, force inner errors into a boxed type to type-erase the inner error, and
wrap the outer error with the inner as its source.&lt;&#x2F;p&gt;
&lt;p&gt;The result: a clean, readable declaration of fallible operations. A
default &lt;code&gt;Error&lt;&#x2F;code&gt; type is provided, but any std &lt;code&gt;Error&lt;&#x2F;code&gt; implementing &lt;code&gt;WithContext&lt;&#x2F;code&gt;
works too.&lt;&#x2F;p&gt;
&lt;p&gt;The core library is tiny. It’s small enough to vendor directly into your project&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The inner boxed error type &lt;code&gt;Frame&lt;&#x2F;code&gt; takes its name from &lt;code&gt;exn&lt;&#x2F;code&gt;. It converts any
error to &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;&#x2F;code&gt; and captures file location via &lt;code&gt;#[track_caller]&lt;&#x2F;code&gt;
for a lightweight stack trace.&lt;&#x2F;p&gt;
&lt;p&gt;With the built-in &lt;code&gt;Error&lt;&#x2F;code&gt; type or the &lt;code&gt;ErrorExt::report()&lt;&#x2F;code&gt; helper, error
trees (yes, trees are supported) render like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; failed&lt;&#x2F;span&gt;&lt;span&gt; to&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; do&lt;&#x2F;span&gt;&lt;span&gt; complex&lt;&#x2F;span&gt;&lt;span&gt; thing&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; at&lt;&#x2F;span&gt;&lt;span&gt; src&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;rs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;19&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; read&lt;&#x2F;span&gt;&lt;span&gt; config&lt;&#x2F;span&gt;&lt;span&gt; file&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; at&lt;&#x2F;span&gt;&lt;span&gt; src&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;rs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;19&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; No&lt;&#x2F;span&gt;&lt;span&gt; such&lt;&#x2F;span&gt;&lt;span&gt; file&lt;&#x2F;span&gt;&lt;span&gt; or&lt;&#x2F;span&gt;&lt;span&gt; directory&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;os&lt;&#x2F;span&gt;&lt;span&gt; error&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;scoped-error&quot;&gt;&lt;code&gt;scoped-error&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; crate also
packs a few extras: a &lt;code&gt;macro_rules!&lt;&#x2F;code&gt; macro for creating common errors that
implement &lt;code&gt;WithContext&lt;&#x2F;code&gt;, and a &lt;code&gt;Many&lt;&#x2F;code&gt; error type for multi-cause errors.&lt;&#x2F;p&gt;
&lt;p&gt;The crate is on crates.io,
source is on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;kanru&#x2F;scoped-error&quot;&gt;Codeberg&lt;&#x2F;a&gt;, and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;scoped-error&#x2F;&quot;&gt;docs&lt;&#x2F;a&gt; cover the details.  If you try it,
I’d love to hear your use cases. File an issue or drop me a note. I’m using
this in my own projects now and finally Rust error handling feels right.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;The core of the library is really tiny.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Copyright (C) 2026 Kan-Ru Chen &amp;lt;kanru@kanru.info&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Any&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;borrow&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Cow&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; StdError&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Debug&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Display&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;panic&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Location&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; A trait for error types that can carry context information.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; trait&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; WithContext&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; StdError&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Any&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Attach a context layer to this error.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; with_context&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; context&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Frame&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Get the location where this error was created or where context&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; was attached.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; location&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Location&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; A single layer of error context.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Frame&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; The underlying error that caused this context.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span&gt; source&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; StdError&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; The location where this context was attached.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span&gt; location&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Location&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; From&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; for&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;where&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    T&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Into&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; StdError&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Creates a `Frame` from any error type, capturing the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; caller&amp;#39;s location.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;track_caller&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; T&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; source&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; value&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;into&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; location&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Location&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;caller&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        Frame&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt; source&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; location&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Low-level function for adding context with a custom error constructor.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;inline&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;always&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; expect_error_fn&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;F&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  err&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; F&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  body&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; FnOnce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Frame&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;where&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    F&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; FnOnce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    E&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; WithContext&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    body&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;map_err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;context&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;with_context&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Add context to errors, returning a custom error type.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;inline&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;always&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; expect_error&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    msg&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Into&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Cow&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    body&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; FnOnce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Frame&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;where&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    E&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; From&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Cow&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Frame&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    body&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;map_err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;context&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;into&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&gt; context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;into&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>處理 Imm32 與 TSF 的相容性問題</title>
        <published>2026-03-06T00:00:00+00:00</published>
        <updated>2026-03-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/debugging-imm32-on-tsf/"/>
        <id>https://kanru.info/debugging-imm32-on-tsf/</id>
        
        <content type="html" xml:base="https://kanru.info/debugging-imm32-on-tsf/">&lt;p&gt;稍微紀錄一下這一次 Debug 的過程。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bei-jing-yu-wen-ti-miao-shu&quot;&gt;背景與問題描述&lt;&#x2F;h3&gt;
&lt;p&gt;Imm32 是 Windows 舊有的輸入法 API，一路沿用到 Windows XP 後被 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Text_Services_Framework&quot;&gt;TSF (Text Service Framework)&lt;&#x2F;a&gt; 取代。許多平台的輸入法支援在切換輸入法時自動送出組字區的文字，例如打字到一半按 Ctrl + Shift 切換英文，可同時完成「送出組字」與「切換輸入法」兩個動作，無需先按 Enter 再切換。這對需快速切換中英文的使用者來說非常方便。&lt;&#x2F;p&gt;
&lt;p&gt;然而，在&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;windows-chewing-tsf&quot;&gt;新酷音 Windows TSF&lt;&#x2F;a&gt; 改用 TSF 實作後，此功能一直時好時壞。若使用者無法預期切換輸入法後未送出的字是「消失」還是「送出」，自然會選擇先按 Enter 再切換，失去便利性。我已多次嘗試修正此問題。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cuo-wu-de-xiu-zheng-fang-xiang&quot;&gt;錯誤的修正方向&lt;&#x2F;h3&gt;
&lt;p&gt;在 TSF API 中，未送出的文字稱為「組字區 (Composition)」，可設定「組字中止 (OnCompositionTerminated)」事件處理器。根據&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;api&#x2F;msctf&#x2F;nf-msctf-itfcompositionsink-oncompositionterminated&quot;&gt;微軟文件&lt;&#x2F;a&gt;，此事件僅在組字被「強迫終止」時觸發，例如開關或切換輸入法。文件提及「輸入法可藉此機會修改組字、還原文字或清除組字」，讓我誤以為必須在此處理器中強制送出組字。雖然修改後看似有效，但在使用 Imm32 API 的軟體中仍無法自動送出。這些軟體依賴系統橋接器使用 TSF 輸入法，行為與原生 TSF 應用不同，我一度認為這是橋接器的限制。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-yong-chu-cuo-gong-ju-zhao-chu-chai-yi&quot;&gt;使用除錯工具找出差異&lt;&#x2F;h3&gt;
&lt;p&gt;一位使用者回報：&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用新酷音輸入法已選字但未按 Enter，若用 Ctrl + Shift 切換輸入法，候選字會消失；但用「Shift 快速切換中英文」時卻會送出。能否增加設定選項？&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;剛好我正在測試日文輸入法，發現 Mozc 無論在哪個軟體都能在切換時自動送出組字。查看其 &lt;code&gt;OnCompositionTerminated&lt;&#x2F;code&gt; 實作，與我的寫法無顯著差異。&lt;&#x2F;p&gt;
&lt;p&gt;於是使用 WinDbg 比較呼叫堆疊（backtrace），發現關鍵差異：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Mozc 的 backtrace：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;OnCompositionTerminated&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;CompComplete&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;IMM32!NotifyIME&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;新酷音 TSF 的 backtrace：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;OnCompositionTerminated&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;CompCancel&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;IMM32!NotifyIME&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;兩者僅在 &lt;code&gt;IMM32!NotifyIME&lt;&#x2F;code&gt; 後分別觸發「完成組字」與「取消組字」，顯示問題根源在系統層級，非輸入法內部可控制。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fa-xian-xi-tong-ceng-ji-de-she-ding-chai-yi&quot;&gt;發現系統層級的設定差異&lt;&#x2F;h3&gt;
&lt;p&gt;這讓我聯想到 ibus 可設定切換時「取消」或「送出」組字。查閱資料後，發現 Imm32 有 &lt;code&gt;IME_PROP_COMPLETE_ON_UNSELECT&lt;&#x2F;code&gt; 屬性，若設定，輸入法在停用時會自動完成組字；否則取消。新酷音的 Imm32 版本有設定此值，但 TSF 橋接器無對應介面。&lt;&#x2F;p&gt;
&lt;p&gt;再拿出 WinDbg 來看看究竟 Mozc 是怎麼被設定的，行為會不一樣？因為這部份是 Windows 的系統函式庫，沒有原始碼可以參考，只好用機器碼單步執行，最後發現有一段機器碼跟 ReactOS 的這一段 TSF 橋接器的&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;reactos&#x2F;reactos&#x2F;blob&#x2F;4cba65d760a730ea4b77ab6fee857e563c0984d8&#x2F;base&#x2F;ctf&#x2F;msctfime&#x2F;msctfime.cpp#L89-L158&quot;&gt;重新實做&lt;&#x2F;a&gt;非常相似。&lt;&#x2F;p&gt;
&lt;p&gt;進一步研究 ReactOS 原始碼發現，TSF 橋接器初始化時會靜態設定此屬性。日文、韓文輸入法預設包含 &lt;code&gt;IME_PROP_COMPLETE_ON_UNSELECT&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;IME_PROP_COMPLETE_ON_UNSELECT &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; IME_PROP_SPECIAL_UI &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;IME_PROP_AT_CARET &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; IME_PROP_NEED_ALTKEY &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;IME_PROP_KBD_CHAR_FIRST&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;但中文輸入法未包含：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;IME_PROP_SPECIAL_UI &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; IME_PROP_AT_CARET &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;IME_PROP_NEED_ALTKEY &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; IME_PROP_KBD_CHAR_FIRST&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;這解釋了為何 Mozc 可行而新酷音不行。由於屬性為靜態設定，似乎無標準方式自訂。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hack-jie-fa-yu-wei-lai-gai-jin-fang-xiang&quot;&gt;Hack 解法與未來改進方向&lt;&#x2F;h3&gt;
&lt;p&gt;在 ReactOS 的 Imm32 實作中，我注意到 &lt;code&gt;ImmLockImeDpi&lt;&#x2F;code&gt; 這個未公開（undocumented）函式，可用於取得 IMM32 內部結構體指標。假設微軟實作類似，便可在初始化後動態修改屬性。實驗後，提交以下變更：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;debug!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;trying to override the default IMM32 property set by MSCTF.dll&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; hkl&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; unsafe&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; GetKeyboardLayout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;0&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; pimedpi&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; unsafe&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; ImmLockImeDpi&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;hkl&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;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; let&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;imedpi&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; unsafe&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt; pimedpi&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;as_mut&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&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    imedpi&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ime_info&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;fdw_property &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; IME_PROP_COMPLETE_ON_UNSELECT&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    debug!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;done adding IME_PROP_COMPLETE_ON_UNSELECT to IME property&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    debug!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;unable to get the PIMEDPI pointer&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pimedpi&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;pimedpi&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;此 hack 目前測試可行。由於 &lt;code&gt;ImmLockImeDpi&lt;&#x2F;code&gt; 是 &lt;code&gt;MSCTF.dll&lt;&#x2F;code&gt; 內部使用的隱藏介面，未來可能變動。為避免執行期錯誤，應改以弱符號（weak symbol）方式動態載入此函式。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xin-de&quot;&gt;心得&lt;&#x2F;h3&gt;
&lt;p&gt;這次除錯讓我體會到，問題有時不在應用層，而在系統與舊有 API 的相容性設計。透過逆向工程與底層探索，雖可突破限制，但也需承擔維護風險。期待未來能有公開 API 支援此類設定。▞&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>開箱 Framework 13 吋 DIY 版筆電</title>
        <published>2024-11-17T00:00:00+00:00</published>
        <updated>2024-11-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/framework-laptop-13-amd/"/>
        <id>https://kanru.info/framework-laptop-13-amd/</id>
        
        <content type="html" xml:base="https://kanru.info/framework-laptop-13-amd/">
            &lt;img src="https://kanru.info/processed_images/z_featured.c00d1adc508646ed.jpg" class="featured" /&gt;&lt;h2 id=&quot;guan-wang-gou-ru&quot;&gt;官網購入&lt;&#x2F;h2&gt;
&lt;p&gt;由於 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;frame.work&quot;&gt;Framework&lt;&#x2F;a&gt; 還沒有在日本開賣，趁著今年回台灣前先預購了 DIY 版的 13 吋 AMD Ryzen™ 7040 系列。這次的 DIY 版本只有加購了記憶體而已，硬碟就用原本買給舊 System76 的 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tech-docs.system76.com&#x2F;models&#x2F;lemp9&#x2F;README.html&quot;&gt;Lemp9&lt;&#x2F;a&gt; 用的 Samsung SSD 970 EVO Plus 2TB，直接繼續使用裡面安裝的 Fedora Workstation 41，所以也沒有另外選擇 OS。&lt;&#x2F;p&gt;
&lt;p&gt;原本想要用日本的信用卡付帳，結帳後卻收到需要人工審核的通知。搜尋論壇了之後發現 Framework 對於購買地區的把關非常嚴格，如果使用沒有支援的國家地區的信用卡有可能導致帳號被鎖定。最後只好先把訂單取消後重新用台灣的信用卡付款。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kai-xiang-zu-zhuang&quot;&gt;開箱組裝&lt;&#x2F;h2&gt;
&lt;p&gt;結帳後五日內出貨，但是等到我回到台灣實際上多等了兩個星期才開箱。&lt;&#x2F;p&gt;
&lt;p&gt;組裝的過程還算順利，只有幾個步驟比較需要注意。第一個是安裝鍵盤模組的時候排線不容易對齊，很怕把插槽壓壞；第二是把鍵盤模組蓋上之後，右下角會微微翹起，看了文件才發現原來是故意這樣設計，讓你拆鍵盤模組時比較容易施力；最後第三點是螢幕轉軸部份的訊號線很容易卡到邊框，調整了好幾次才確定闔上螢幕的時候沒有任何凸起的地方。&lt;&#x2F;p&gt;
&lt;p&gt;組裝好之後順利開機。附上 Fastfetch 的螢幕快照一張：&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;framework-laptop-13-amd&#x2F;fastfetch.png&quot;
      alt=&quot;Fastfetch 螢幕快照&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; Fastfetch 螢幕快照
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;ce-shi-yu-she-ding&quot;&gt;測試與設定&lt;&#x2F;h2&gt;
&lt;p&gt;實際使用了幾天都沒有什麼問題，所有的功能都是開箱就可以使用，不用任何微調，甚至連電源開關上面的指紋感應都可以用，這是我第一次在 Linux 筆電上面成功使用指紋解鎖。&lt;&#x2F;p&gt;
&lt;p&gt;以下會隨時更新紀錄後來有調整的地方。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xiu-mian-gong-neng-yu-dian-chi-zui-jia-hua&quot;&gt;休眠功能與電池最佳化&lt;&#x2F;h3&gt;
&lt;p&gt;休眠以及使用時的耗電量是論壇上數一數二被討論的話題&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;。有人分析到記憶體的容量大小會影響休眠時的耗電量，尤其是 48GB 的記憶體的耗電量是 8GB 的十倍&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;。同一個討論串裡面也提到他可以休眠 7.82 天但是不甚滿意，因為他的其他筆電可以休眠到 14 天。我的目標只要可以達到 7 天左右就好，因為之前用的 Lemp9 只能休眠 1.2 天左右，我已經對長時間休眠沒有抱很大期望了。&lt;&#x2F;p&gt;
&lt;p&gt;用 AMD 工程師分享的 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;drm&#x2F;amd&#x2F;-&#x2F;blob&#x2F;master&#x2F;scripts&#x2F;amd_s2idle.py&quot;&gt;amd_s2idle.py&lt;&#x2F;a&gt; 這隻程式來分析休眠有沒有成功、可能的問題、實際的耗電量等等。第一次跑得時候發現只有 60% 的時間硬體是在低耗電模式，把所有的喚醒來源都關掉後成功讓 99% 休眠時間都是低耗電模式。懷疑是裝在左下跟右下的兩個 USB-A 的擴充卡造成的（論壇上也有不少人提到 USB-A 的問題），決定先全部換成 USB-C。&lt;&#x2F;p&gt;
&lt;p&gt;另外，Fedora 從 41 版之後就把控制節能模式的 power-profiles-daemon 換成 tuned-ppd，但是 power-profiles-daemon 有同一位 AMD 工程師 Mario Limonciello 的加持，感覺會有比較多深入的最佳化，所以我把 tuned-ppd 換回 power-profiles-daemon 了。&lt;&#x2F;p&gt;
&lt;p&gt;從 EFI 設定界面可以直接設定電池最多充到 80% 就自動停止充電，目前從 80% 放了一晚之後大約剩下 74%，換算大概可以休眠 4 到 5 天左右，算是很夠用了。不過我還是很好奇的持續在看有沒有可以繼續最佳化的方法&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ying-mu-diao-zheng-zui-jia-hua&quot;&gt;螢幕調整最佳化&lt;&#x2F;h3&gt;
&lt;p&gt;我選購的是新的 2.8K 霧面螢幕，更新率可達 120 Hz，但是我平常的使用情境都不是會有東西動來動去的，所以把更新頻率設定改回 60 Hz，省省電。希望之後 GNOME 的動態更新頻率功能可以自動最佳化這個部份。&lt;&#x2F;p&gt;
&lt;p&gt;這塊面板可以支援到接近 100% sRGB 的顏色範圍，可以從 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.notebookcheck.net&#x2F;Framework-Laptop-13-5-Core-Ultra-7-review-New-2-8K-120-Hz-display-with-Arc-8-graphics.874187.0.html&quot;&gt;notebookcheck&lt;&#x2F;a&gt; 下載他們的 ICM 檔案來套用螢幕顏色校正。套用後原本偏藍的畫面變成溫暖一點的色調。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;she-ding-opencl&quot;&gt;設定 OpenCL&lt;&#x2F;h3&gt;
&lt;p&gt;AMD 的內建 GPU 還算不錯，雖然無法跑運算量大的遊戲，但是足夠我用在 Darktable 的 OpenCL 加速了，這也是我選擇 AMD 版本的原因之一。&lt;&#x2F;p&gt;
&lt;p&gt;Fedora 上要啟用 OpenCL 可以參考 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fedoraproject.org&#x2F;wiki&#x2F;SIGs&#x2F;HC#Installation&quot;&gt;Fedora HC SIG&lt;&#x2F;a&gt; 的 wiki：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; usermod&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;G&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; render,video&lt;&#x2F;span&gt;&lt;span&gt; $&lt;&#x2F;span&gt;&lt;span&gt;LOGNAME&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; dnf&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; rocm-opencl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; dnf&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; clang18&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; workaround 一個 clang path 的問題&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;重開機後就可以執行 &lt;code&gt;darktable-cltest&lt;&#x2F;code&gt; 來看看 OpenCL 有沒有正確啟用。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;接下來這台 Framework 就會是我的主力囉！▞&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;community.frame.work&#x2F;t&#x2F;real-world-battery-life-experience-fw13-amd&#x2F;39112&#x2F;73&quot;&gt;https:&#x2F;&#x2F;community.frame.work&#x2F;t&#x2F;real-world-battery-life-experience-fw13-amd&#x2F;39112&#x2F;73&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;community.frame.work&#x2F;t&#x2F;impact-of-ram-density-on-suspend-power-consumption&#x2F;57664&quot;&gt;https:&#x2F;&#x2F;community.frame.work&#x2F;t&#x2F;impact-of-ram-density-on-suspend-power-consumption&#x2F;57664&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;community.frame.work&#x2F;t&#x2F;is-framework-13-amd-s2idle-modern-standby-efficient-enough&#x2F;46431&quot;&gt;https:&#x2F;&#x2F;community.frame.work&#x2F;t&#x2F;is-framework-13-amd-s2idle-modern-standby-efficient-enough&#x2F;46431&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>回顧用 Rust 重寫新酷音的經驗</title>
        <published>2024-05-05T00:00:00+00:00</published>
        <updated>2024-05-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/libchewing-rust-rewrite-in-review/"/>
        <id>https://kanru.info/libchewing-rust-rewrite-in-review/</id>
        
        <content type="html" xml:base="https://kanru.info/libchewing-rust-rewrite-in-review/">
            &lt;img src="https://kanru.info/processed_images/z_featured.35e67356d34ceb09.jpg" class="featured" /&gt;&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;&quot;&gt;libchewing&lt;&#x2F;a&gt; 0.8.0 將是新酷音的重大里程碑。酷音原本是 XCIN 的一個模組，新酷音團隊在 2002 年把模組獨立出來變成一個共有函式庫 (shared library) 維護至今。從 2021 年開始我就斷斷續續的用 Rust 重寫 libchewing，現在終於要預設啟用 Rust 重寫的版本了！我現在一邊為釋出做最後的準備工作，一邊寫個文章紀錄一下整個重寫的經驗，順便最後測試實際使用的情境。這篇文章將會以紀錄發生了什麼事為主，不會解釋太多技術細節，如果有想要深入了解的部份，歡迎留言讓我知道。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2017-nian-de-di-yi-ci-chang-shi&quot;&gt;2017 年的第一次嘗試&lt;&#x2F;h2&gt;
&lt;p&gt;應該是在 2017 年八月的時候，那個時候剛剛接觸 Rust 沒有多久，就想著可以用新酷音練練功，試著把使用者詞庫的模組 (userphrase) 改用 Rust 的 HashMap 改寫。那個時候新酷音的建置環境還是用 autotools 以及 libtool，重寫的結果算是證明了可以把 C 跟 Rust 放在同一個程式碼庫內一起編譯使用。練習的結果在 COSCUP 給了一個經驗分享 (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=fLqGZJflod4&quot;&gt;COSCUP 2017 – Porting C Library to Rust, 以 libchewing 為例&lt;&#x2F;a&gt;)。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2021-nian-di-zhong-xin-kai-shi-de-qi-ji&quot;&gt;2021 年底重新開始的契機&lt;&#x2F;h2&gt;
&lt;p&gt;2021 年底的時候開始玩流行的寫程式解謎遊戲 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;adventofcode.com&#x2F;&quot;&gt;Advent of Code&lt;&#x2F;a&gt;，依照我的慣例是用 Rust 寫 (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kanru&#x2F;adventofcode2021&quot;&gt;adventofcode2021&lt;&#x2F;a&gt;)。寫著寫著不禁想，同樣是透過寫程式娛樂，我不如把時間花在寫一些對其他人有幫助的程式。同樣是 2021 年接觸了 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kanru&#x2F;qmk_firmware&quot;&gt;QMK&lt;&#x2F;a&gt; 與 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kanru&#x2F;hhkb-zmk&quot;&gt;ZMK&lt;&#x2F;a&gt;，覺得好像可以把寫鍵盤 firmware 的經驗應用在酷音的鍵盤對應表上，於是又開始想要怎麼把新酷音用 Rust 改寫。&lt;&#x2F;p&gt;
&lt;p&gt;這時候只是開始隨便寫寫，驗證自己心裡所想的架構可以運作，並重新閱讀 libchewing 的程式碼，重拾對程式碼的了解。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2022-nian-huang-jin-zhou-que-li-ji-chu-jia-gou&quot;&gt;2022 年黃金週確立基礎架構&lt;&#x2F;h2&gt;
&lt;p&gt;4&#x2F;29 為期一週的黃金週假期開始，在 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt; 上面開了新的聊天室，把&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.to&#x2F;#&#x2F;#libchewing:matrix.org&quot;&gt;聊天室的連結&lt;&#x2F;a&gt;加到 README 之後就開始先加入 CMake 的 FindRust 模組，讓 libchewing 可以連結 Rust 的程式碼，然後就陸續把許氏鍵盤、表準鍵盤、倚天26、大千鍵盤等等鍵盤模組實做出來並且替換 C 的內部 API，採取的是從最末端的程式碼一步一步的取代更大的 C 模組。(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kanru&#x2F;libchewing&#x2F;commits&#x2F;rust&#x2F;?since=2022-04-29&amp;amp;until=2022-05-02&quot;&gt;開發紀錄&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;此時要感謝 czchen 幫新酷音加了非常多的測試，我可以直接用原本的測試程式來確保大致功能是對的。要不是有這些測試程式，我的進度一定會要慢上好幾倍。而且這些測試很多都是整合測試 (integration test)，讓我可以不必理會原本 C 是怎麼實做的，只需要依照 API 界面實做即可跟原本的程式合在一起測試。&lt;&#x2F;p&gt;
&lt;p&gt;等所有的注音輸入模組都寫的差不多之後，我開始寫字典的讀取查找模組，這是酷音裡面重要又有一定複雜度的模組，但是又相對的獨立沒有其他的相依性，所以是很好的重構目標。首先我實做了一個新的 Trie 檔案格式以及字典建構工具，然後又寫了 SQLite 為底的使用者字典格式。此時很多時候都是在逆向工程原本字典模組 (tree.c) 的一些規則，像是排序的順序、詞頻的調整等等。原本的 tree.c 用的是記憶體映射 (mmap) 的方式把字典檔案直接當作記憶體位置操作，直接回傳一個唯讀的記憶體指標，我在重構的時候就直接分配新的記憶體然後不釋放，等到測試都過了之後再慢慢的修 valgrind 報的記憶體洩漏錯誤。(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kanru&#x2F;libchewing&#x2F;commits&#x2F;rust&#x2F;?since=2022-05-03&amp;amp;until=2022-05-28&quot;&gt;開發紀錄&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;此時我已經把新酷音內最沒有其他相依的兩大模組換成 Rust 了，我也漸漸有信心好像可以完成這個「小專案」。有了字典模組之後，接下來就是需要使用字典模組的注音分詞的酷音模組了，這邊的演算法我一直不是很有信心，加上工作變忙於是就&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kanru&#x2F;libchewing&#x2F;commit&#x2F;90027c908829e4df268c2ce3c5dc7c0774f6af99&quot;&gt;慢慢的摸索&lt;&#x2F;a&gt;，直到 12 月又到了 Advent of Code 的時間才又開始有精力開發。把酷音的演算法移植到 Rust 之後很令人驚訝的是大部份的測試都過了，既開心有不錯的進展，卻又有點擔心已有的測試在分詞模組的覆蓋率有點不足。我要感謝聰明又可愛的老婆不厭其煩的跟我討論我最沒有信心的演算法。(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kanru&#x2F;libchewing&#x2F;commits&#x2F;rust&#x2F;?since=2022-12-23&amp;amp;until=2023-01-05&quot;&gt;開發紀錄&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2023-nian-he-bing-dao-zhu-xian-kai-fa&quot;&gt;2023 年合併到主線開發&lt;&#x2F;h2&gt;
&lt;p&gt;把詞庫跟分詞模組都重構完之後，就有機會把原本兩個模組之間黏合用的 C API 拿掉了。兩個模組之間用新設計的 Rust API 組合之後，整個新酷音除了使用者的輸入輸出的部份都已經是 Rust。我開始整理 commit history 然後把結果提交為 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;pull&#x2F;347&quot;&gt;pull request&lt;&#x2F;a&gt;，並分享未來的開發計畫。此時也已經換成用 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;corrosion-rs&#x2F;corrosion&quot;&gt;Corrosion-rs&lt;&#x2F;a&gt; 這個優秀的 CMake 專案來編譯 libchewing。採用的是 Rust staticlib + dummy C file + version script 的方式來產生共用函式庫。&lt;&#x2F;p&gt;
&lt;p&gt;提交 pull request 之後，感謝眾多的 reviewer 給予建議，我也開了許多 ticket 供想要參與的人可以幫忙或是尋求指導。Pull request 裡面的參考架構仍然接近現在所使用的架構：&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;libchewing-rust-rewrite-in-review&#x2F;architecture.png&quot;
      alt=&quot;架構圖&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 架構圖
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;接下來開始設計 Editor (input&#x2F;output) 模組，這個模組跟其他模組不一樣的地方是它有著非常多的 API 以及內部資料狀態 (state)，沒有辦法像其他模組一樣漸進式的處理。原本 libchewing 的 chewingio.c 與 chewingutil.c 等，總共有約 119 個 API，全部都是透過操作內部狀態以及使用詞庫和分詞模組來實做新酷音的主要功能，這些 API 全部都共用同一組狀態，存在 ChewingContext 裡面，所以沒辦法單單只把一些功能搬到 Rust。&lt;&#x2F;p&gt;
&lt;p&gt;後來因為注意到在跑 test 的時候，Rust 的版本比純 C 的版本要慢上一個數量級，所以花了些時間用 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mstange&#x2F;samply&#x2F;&quot;&gt;samply&lt;&#x2F;a&gt; 來了解問題在哪裡〔很多時間是花在原本較沒效率的 syllable 設計，後來把內裝改成用 u16 之後改善不少〕。我從 7 月開始除了偶爾構思一下暫存編輯區 (preedit text)、候選字清單 (candidate window)、編輯區邏輯 (editor state machine) 的架構之外，都花時間在最佳化 Trie 字典的效能，逃避不想面對的複雜重構。&lt;&#x2F;p&gt;
&lt;p&gt;11 月參考 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hoverbear.org&#x2F;blog&#x2F;rust-state-machine-pattern&#x2F;&quot;&gt;hoverbear&lt;&#x2F;a&gt; 的 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hoverbear.org&#x2F;blog&#x2F;rust-state-machine-pattern&#x2F;&quot;&gt;blog&lt;&#x2F;a&gt; 設計了新的 state machine 架構，花了些時間把 C 的 119 個公開 API 用 Rust 重寫，把 API 跟 Editor 跟字典模組直接整合起來，得到一個只有用到 Rust 程式碼但是測試壞掉大半的狀態。接下來就是一個一個修 test-bopomofo.c 的錯誤。我幾乎都是把原本的 C 實做當作黑盒子做 TDD，因為如果依照原本的邏輯實做，會受到原本架構的影響。一旦有了可用的 state machine 架構之後，我就以還算穩定的速度在年底前就把所有的測試都通過了。(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;commits&#x2F;master&#x2F;?since=2023-11-20&amp;amp;until=2023-12-30&amp;amp;before=d7f5d9a0e3fdcf31ba259c46e04e758a4369217b+35&quot;&gt;開發紀錄&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2024-kai-shi-wen-gu-ji-chu&quot;&gt;2024 開始穩固基礎&lt;&#x2F;h2&gt;
&lt;p&gt;等到測試都通過之後改用 genkeystroke 測試一般的使用情境，此時會發現雖然有很多小地方都還沒有處理〔例如選詞結果不會清除〕，已經可以通過所有的測試以及一般的使用情境都沒有問題。這證實我之前的猜測，現有的測試只能帶我到大約 80% 的地方，剩下的 20% 需要靠其他的方法測試才行。&lt;&#x2F;p&gt;
&lt;p&gt;所有的測試都通過之後，表示 Rust 版本的酷音已經可以用在基本的使用情境了，也可以編譯出只有使用 Rust 的 libchewing.so 共用函式庫。接下來需要更廣泛的測試，首先在 CI 上面在所有平台同時測試 C 跟 Rust 版本，以免有任何的退步。一月底的時候釋出 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;releases&#x2F;tag&#x2F;v0.6.0&quot;&gt;0.6.0&lt;&#x2F;a&gt; 為 2016 年以來的第一個維護版同時也是第一個 alpha 版的 Rust rewrite 版。 (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;commits&#x2F;master&#x2F;?since=2023-12-31&amp;amp;until=2024-01-31&amp;amp;after=d7f5d9a0e3fdcf31ba259c46e04e758a4369217b+34&quot;&gt;開發紀錄&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;0.6.0 釋出之後就開始準備可以日常使用、抓蟲的 0.7.0 版，根據之前定義的路線圖這個版本有以下重點項目：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;可以自動的搬遷使用者字典到新的格式&lt;&#x2F;li&gt;
&lt;li&gt;穩定性優先，盡量不可以有嚴重錯誤或是毀壞使用者資料&lt;&#x2F;li&gt;
&lt;li&gt;減少不必要的相依項目&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;穩定性的部份，我從一開始就希望可以用 fuzzing 的方式幫我找到更多的 bug，最後我用 clang + &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-fuzz&#x2F;afl.rs&#x2F;&quot;&gt;afl.rs&lt;&#x2F;a&gt; 的方式編譯出有 instrument 的執行檔，讓 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aflplus.plus&#x2F;&quot;&gt;AFL++&lt;&#x2F;a&gt; 可以更有效率的執行。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;libchewing-rust-rewrite-in-review&#x2F;afl.png&quot;
      alt=&quot;AFL++ 跑了九天的截圖&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; AFL++ 跑了九天的截圖
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;用了 AFL++ 之後很快就找到許多原本的測試沒有找到的問題，大多都是因為原本的 state machine 沒有阻擋把游標移動到超出編輯區的頭尾，或是選擇鍵超出頁面範圍。原本這些問題在 C 的版本會非常難偵測，但是因為 Rust 有邊界測試以及數值溢位的偵測，這些問題都可以很簡單的發現並修復。持續的跑 fuzzer 好幾天都沒有發現新的問題之後，我更有信心日常使用使用者不會遇到程式嚴重錯誤的問題，最多只是程式的輸出結果很奇怪。(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;commits&#x2F;master&#x2F;?since=2024-03-13&amp;amp;until=2024-03-21&quot;&gt;開發紀錄&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;※ 一個用 fuzzer 找 bug 的小技巧，我在日常測試的時候出現一個問題，就是編輯區的詞有時候會消失，但是不知道要怎麼重現，我就在 fuzzer 裡面特別加了一個檢查，很快 fuzzer 就找到好幾個 testcase，原來是我根本就沒有處理選詞之後的重新整理，沒想到這樣也可以用到現在。&lt;&#x2F;p&gt;
&lt;p&gt;在準備 0.7.0 正式 release 之前，我決定替換字典的實做。一開始在設計架構的時候預留了可以有多種不同字典格式的設計，也實際實做了 SQLite, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;chewing&#x2F;latest&#x2F;chewing&#x2F;dictionary&#x2F;struct.TrieBuilder.html#file-format&quot;&gt;Trie&lt;&#x2F;a&gt;, CDB 三種不同的字典格式，其中 SQLite 是可以更新的，CDB 雖然是唯讀的格式，可是利用一個技巧把需要更新的內容先快取在記憶體內，然後定期的覆蓋硬碟上的字典檔，也可以達到更新的效果。後來在做&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;issues&#x2F;478&quot;&gt;效能測試&lt;&#x2F;a&gt;的時候發現建立 Trie 沒有比寫新的 CDB 檔案更花時間，所以如果用一樣的技巧就可以把 Trie 格式當作可更新的使用者字典來用，於是就有想法把系統字典與使用者字典全部統一成一個格式，這樣可以讓整體的相依更少。&lt;&#x2F;p&gt;
&lt;p&gt;最後，2024 年的黃金週現在準備釋出 0.8.0，把 Rust 重寫的版本變成預設選項。把 chewing-cli 開發成一個通用工具程式，包含原本用來建立系統辭典的 init-database 程式，並加上新的 info 與 dump 兩個命令，可以用來檢視字典的內容。這個版本還用了一個 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;commit&#x2F;06e0073fee373f0d79ab1489f56aab62ee8632ed&quot;&gt;K Shortest Path&lt;&#x2F;a&gt; 演算法 (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Yen&amp;#x27;s_algorithm&quot;&gt;Yen’s Algorithm&lt;&#x2F;a&gt;) 來加強搜尋候選斷詞清單的最差時間複雜度。(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;libchewing&#x2F;commits&#x2F;master&#x2F;?since=2024-03-22&amp;amp;until=2024-05-04&quot;&gt;開發紀錄&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zui-chu-cai-yong-que-mei-you-liu-xia-de-she-ji&quot;&gt;最初採用卻沒有留下的設計&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Tracing.&lt;&#x2F;strong&gt; 原本用了 Tokio 的 tracing crate，可以很方便的幫 function 加上 tracing log，對於實做 Editor 的 state machine 幫助極大，可是後來的 log 需求比較簡單，於是就改回相依項目比較少的 log crate。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;RIFF.&lt;&#x2F;strong&gt; 一開始設計的新 Trie 字典格式仿照了 WebP 的格式標準，用了 RIFF 容器格式作為可擴充的檔案格式基礎，把 index 與 data 封裝在同一個檔案內，後來在重新設計的時候因為需要更有彈性的儲存詞彙資料而改用 ASN.1 描述檔案格式，並用 DER 做為檔案的實做格式，就沒有用 RIFF 了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;CRC32.&lt;&#x2F;strong&gt; 原本的 Trie 字典格式裡面用了許多自訂的 TLV 封包，在做 fuzzing 的時候發現有很多邊界測試需要做，於是就在 index 跟 data 資料的最後面加上 CRC32 的檢查碼，然後就在程式裡面假設如果檢查碼通過的話就當作資料都是正確的，省掉許多的額外邊界測試，算是有點偷懶。後來因為 TLV 太麻煩不想處理，直接用標準的 TLV 格式，也就是 ASN.1 的 DER 格式，也就沒有一定要用 CRC32 檢查碼了。之後可能會加回來讓檔案更牢不可破。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;CDB.&lt;&#x2F;strong&gt; 一開始設計來取代 uhash.dat 格式的，用了 DJB 的 CDB 格式，跟原本的 uhash 一樣把字典當作 key&#x2F;value 存。如同前面提到的，因為把字典格式統一為 Trie 與 TrieBuf 之後就沒有用 CDB 了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;SQLite.&lt;&#x2F;strong&gt; 最新的 libchewing 是用 sqlite3 當作使用者字典的格式，好處是有支援多行程，ACID，然後其他的工具或是使用者可以直接操作 sqlite3 檔案。最一開始改寫的時候重新設計了 schema 讓 sqlite 更有效率，並且支援大於 10 個字元的詞彙詞。後來因為想要減少額外的相依項，重新設計讓 TrieBuf 格式也可以更新之後，就打算移除 SQLite 格式了，目前因為需要可以從舊有格式轉移，這部份的程式碼還留著。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Symbol versioning.&lt;&#x2F;strong&gt; 一開始在實做只用 Rust 編譯整個 libchewing 的時候，採用的是用 cmake 去呼叫 linker 來產生最後的 shared library，因為這樣可以更容易的控制哪一些 symbol 是 public 的，這時候我用了 version script 來控制 ELF library 的 symbol version，後來因為想用 cargo + rustc 直接產生 shared library，而 rustc 又不支援用 version script，所以就暫時放棄了 symbol version 的想法。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gan-xie&quot;&gt;感謝&lt;&#x2F;h2&gt;
&lt;p&gt;首先感謝我自己在 2004 年寫的 testchewing 與 genkeystroke 兩個小程式，可以在沒有 GUI 或是任何 IM wrapper 的情況下直接測試 libchewing，同時 genkeystroke 可以用來產生測資，而 testchewing 可以用來執行 genkeystroke 產生的測資。酷音的很多測試用的也是 testchewing 所使用的檔案格式。這兩個程式仍然是我測試 libchewing 時不可或缺的工具。&lt;&#x2F;p&gt;
&lt;p&gt;接下來要感謝 czchen，他替 libchewing 加了 cmake 的支援，以及大量的測試。若是沒有這兩者，我根本無法想像開始用 Rust 改寫的計畫。我的改寫基本上都是 TDD (Test Driven)，全部都歸功於這些測試。感謝我一開始的決定先把 GitHub Actions 的 CI 設定好同時跑 C 跟 Rust 的整合測試以及計算覆蓋率，我在重構的時候以及有人貢獻的時候都可以有明確的品質指標。目前整個程式庫大約有 88% 的測試覆蓋率。&lt;&#x2F;p&gt;
&lt;p&gt;感謝 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.tw&#x2F;&quot;&gt;Rust Taiwan&lt;&#x2F;a&gt; 的 wusyong 與 weihanglo 幫忙 review 最初的 pull request，以及其他夥伴在 Matrix&#x2F;Telegram 上提供的各種建議。&lt;&#x2F;p&gt;
&lt;p&gt;感謝 rano 開發直接使用 chewing-rs Rust API 的實驗性 IM &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rano-oss&#x2F;chewingwl&quot;&gt;chewingwl&lt;&#x2F;a&gt; 並在 Matrix 上面給予回饋。&lt;&#x2F;p&gt;
&lt;p&gt;最後要感謝 yan12125 與 wengxt 幫忙實際測試 libchewing 0.8.0 並回報問題，jserv 幫忙更新 README 裡面新酷音的歷史以及現狀。▞&lt;&#x2F;p&gt;
&lt;p&gt;※ 封面照片是 2022 年黃金週去東京野鳥園玩的時候拍的螃蟹&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>網站搬到 HostHatch</title>
        <published>2024-02-11T00:00:00+00:00</published>
        <updated>2024-02-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/host-moved-to-hosthatch/"/>
        <id>https://kanru.info/host-moved-to-hosthatch/</id>
        
        <content type="html" xml:base="https://kanru.info/host-moved-to-hosthatch/">
            &lt;img src="https://kanru.info/processed_images/z_featured.fb1d8d5b3297ebb4.jpg" class="featured" /&gt;&lt;p&gt;TL;DR &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hosthatch.com&#x2F;products&quot;&gt;HostHatch&lt;&#x2F;a&gt; 便宜好用，想測試的話可以用我的連結 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloud.hosthatch.com&#x2F;a&#x2F;3823&quot;&gt;https:&#x2F;&#x2F;cloud.hosthatch.com&#x2F;a&#x2F;3823&lt;&#x2F;a&gt; 這樣我可以收到一點回饋 🙂&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;幾年前因為想要用 Vultr 的機器玩 BGP，就把網站從 AWS 搬到 Vultr 的東京機房。當初用的是稍微高一級的 1c1g，夠同時跑多個 container 沒有問題，於是就用一台當 blog + BGP router。&lt;&#x2F;p&gt;
&lt;p&gt;去年原本租了 Element 代管的 ElementOne 來玩 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt; 順便贊助開發。後來 Element 取消這個每月 $10 的方案不做一般消費者這塊了，剛好我也在考慮自己裝個 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix-org.github.io&#x2F;synapse&#x2F;latest&#x2F;welcome_and_overview.html&quot;&gt;Synapse&lt;&#x2F;a&gt; 來自架 Matrix，就把這筆預算拿來用在新的 VM 上面。&lt;&#x2F;p&gt;
&lt;p&gt;考慮到 Synapse 跟大型的聊天室直接對接的時候好像還滿需要資源的，需要找一個可以跑 PostgreSQL 的規格。這樣整個 VM 就會有至少 caddy, php-fm, mariadb, synapse, postgresql 這幾個 container，感覺要至少 2c4g 才夠。Vultr 符合這個規格最便宜的就是 $20 的共用 CPU 方案。&lt;&#x2F;p&gt;
&lt;p&gt;因為種種原因我想要找有沒有可能加一點錢改用獨立主機，這樣可以把更多小網站集中起來，於是就用以下的條件開始找：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;在東京&lt;&#x2F;li&gt;
&lt;li&gt;便宜的獨立主機，大約 20 多美金&lt;&#x2F;li&gt;
&lt;li&gt;有 BGP 可以用自己的 ASN&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;說實在想要找到這種規格的在東京幾乎是沒有，倒是有不少 IP 假裝是在東京，其實機房是在美西的便宜方案想要魚目混珠，又或是機房看起來是在東京沒錯，但是 peer 的狀況實在有夠差，我家距離附近主要的 Equinix 機房都在一兩公里的距離而已，但是封包常常要先繞去大阪或是美西再回東京，130 ms 到 200 ms 的 latency 只好放棄。&lt;&#x2F;p&gt;
&lt;p&gt;HostHatch
後來尋覓到 HostHatch 最近剛好開始提供 BGP 的服務。雖然一開始需要設定費，但是他們提供的報價看起來非常的物超所值。$15 的價錢就可以買到 4c16g，其中還有 2c 是獨立的。空間與流量也算滿大方的，雖然東京的流量比其他地方少一半，以我的用量來說也還算夠用，算是很不錯的 trade off。&lt;&#x2F;p&gt;
&lt;p&gt;只是在 LowEndTalk 或是 LowEndSprit 討論區上面給 HostHatch 的評價都不是很好，不外乎回覆很慢，服務態度不好之類的。仔細觀察感覺很有可能是因為這些人都買促銷價的 VM 所以也得到最基本的客服而已。&lt;&#x2F;p&gt;
&lt;p&gt;總之先開了帳號，直接從網站上買了不是促銷的 $22 方案 6c24g, 100G, 6T 一個月來試用看看，然後開始設定。雖然 BGP 啟用的部份一開始有點小差錯，考慮到他們才剛開始做這塊也算是情有可原。整體而言服務的品質其實相當不錯，回訊息都很快，機器的規格也沒有灌水。從我家連到機器只需要 4 ms 的時間。&lt;&#x2F;p&gt;
&lt;p&gt;測試了幾天之後就把網站搬過來，然後也架好 Synapse，目前跑了快三個月都沒什麼問題，也測試了從台灣連過來的速度也不錯，走 GSL 只需要 34 ms 左右的時間。&lt;&#x2F;p&gt;
&lt;p&gt;如果有興趣想試試看的人可以用我的連結 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloud.hosthatch.com&#x2F;a&#x2F;3823&quot;&gt;https:&#x2F;&#x2F;cloud.hosthatch.com&#x2F;a&#x2F;3823&lt;&#x2F;a&gt; 這樣我可以收到一點回饋 🙂&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>IBus Chewing 更新</title>
        <published>2024-02-05T00:00:00+00:00</published>
        <updated>2024-02-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/ibus-chewing-update-20240205/"/>
        <id>https://kanru.info/ibus-chewing-update-20240205/</id>
        
        <content type="html" xml:base="https://kanru.info/ibus-chewing-update-20240205/">
            &lt;img src="https://kanru.info/processed_images/z_featured.16f1a522a528c4ee.jpg" class="featured" /&gt;&lt;p&gt;上週末跟 Ding-Yi Chen 聯絡上，把 ibus-chewing 的 repository 搬到 Chewing Project 之後，就動手改一些東西。新的 repo 網址是 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;ibus-chewing&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;ibus-chewing&lt;&#x2F;a&gt; 。&lt;&#x2F;p&gt;
&lt;p&gt;首先著重在整理 code base，先把 CMake 環境整理一下，然後把原本手刻的 ibus-setup-chewing 用 GTK4 重寫，並引入 libadwaita 讓界面跟 gnome 統一。&lt;&#x2F;p&gt;
&lt;p&gt;之前的樣子：&lt;&#x2F;p&gt;

&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;ibus-chewing-update-20240205&#x2F;Screenshot-from-2024-02-04-08-58-09.png&quot;
        width=&quot;200&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;ibus-chewing-update-20240205&#x2F;Screenshot-from-2024-02-04-08-58-16.png&quot;
        width=&quot;200&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;ibus-chewing-update-20240205&#x2F;Screenshot-from-2024-02-04-08-58-21.png&quot;
        width=&quot;200&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;p&gt;現在的樣子：&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;ibus-chewing-update-20240205&#x2F;Screenshot-from-2024-02-05-23-02-57.png&quot;
      alt=&quot;新 ibus-chewing 設定界面&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 新 ibus-chewing 設定界面
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;主要的兩個 PR:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;ibus-chewing&#x2F;pull&#x2F;171&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;ibus-chewing&#x2F;pull&#x2F;171&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;ibus-chewing&#x2F;pull&#x2F;172&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;chewing&#x2F;ibus-chewing&#x2F;pull&#x2F;172&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;接下刪掉約三千多行的沒有在用的程式碼，拿掉 X11 的依賴，修掉一些小的 bug。接下來測試若是沒什麼大問題，就可以來準備出個新版。▞&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>2024 年的專案計畫</title>
        <published>2024-01-21T00:00:00+00:00</published>
        <updated>2024-01-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/2024-floss-contribution-plan/"/>
        <id>https://kanru.info/2024-floss-contribution-plan/</id>
        
        <content type="html" xml:base="https://kanru.info/2024-floss-contribution-plan/">
            &lt;img src="https://kanru.info/processed_images/z_featured.b5daca261c11d03d.jpg" class="featured" /&gt;&lt;p&gt;有太多想做的小專案，隨手紀錄一下以免要一直回想。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xin-ku-yin-rust-ban&quot;&gt;新酷音 Rust 版&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;0.7.0 – Rust 重寫的版本可以支援全部功能
&lt;ul&gt;
&lt;li&gt;考慮一下最終要支援的資料庫格式們&lt;&#x2F;li&gt;
&lt;li&gt;Fuzzing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;0.8.0 – 預設使用 Rust 版本&lt;&#x2F;li&gt;
&lt;li&gt;0.9.0 – 第一版 Rust API 的半穩定版
&lt;ul&gt;
&lt;li&gt;重新設計 Editor type&lt;&#x2F;li&gt;
&lt;li&gt;重新設計 Error type&lt;&#x2F;li&gt;
&lt;li&gt;重新設計 Keymap module&lt;&#x2F;li&gt;
&lt;li&gt;更新文件&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;xin-ku-yin-xin-gong-neng&quot;&gt;新酷音新功能&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;簡單注音模式&lt;&#x2F;li&gt;
&lt;li&gt;只輸入聲符的模式&lt;&#x2F;li&gt;
&lt;li&gt;不需要輸入音調的模式&lt;&#x2F;li&gt;
&lt;li&gt;好用的詞庫編輯器，從自己文章產生詞庫等等&lt;&#x2F;li&gt;
&lt;li&gt;多詞庫功能&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;cdb2-rs&quot;&gt;cdb2-rs&lt;&#x2F;h2&gt;
&lt;p&gt;把這個 fork 的 library 整理一下釋出新版本&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ibus-z&quot;&gt;ibus-z&lt;&#x2F;h2&gt;
&lt;p&gt;用 zbus 寫一個 ibus 的 wrapper library&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ibus-chewing&quot;&gt;ibus-chewing&lt;&#x2F;h2&gt;
&lt;p&gt;更新 ibus-chewing 到 GTK4&lt;&#x2F;p&gt;
&lt;p&gt;推動 Fedora 把繁體中文的輸入法改回 ibus-chewing&lt;&#x2F;p&gt;
&lt;p&gt;用 Rust 重寫 ibus-chewing，需要 chewing 0.8.0 以及 ibus-z&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mupdf&quot;&gt;MuPDF&lt;&#x2F;h2&gt;
&lt;p&gt;Update the Debian package to build shared library by default.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>Fedora 上使用硬體加密的 SSH 與 PGP</title>
        <published>2023-12-02T00:00:00+00:00</published>
        <updated>2023-12-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/fedora-pkcs11-ssh-and-pgp/"/>
        <id>https://kanru.info/fedora-pkcs11-ssh-and-pgp/</id>
        
        <content type="html" xml:base="https://kanru.info/fedora-pkcs11-ssh-and-pgp/">
            &lt;img src="https://kanru.info/processed_images/z_featured.3b016985ca5ca8b5.png" class="featured" /&gt;&lt;p&gt;從一開始入手 Yubikey 4，後來換成 Yubikey 5 NFC，我已經很久沒有把 SSH 跟 PGP 的密鑰放在檔案系統裡過了。我最近新買了一隻 Yubikey 5 Nano，簡單設定後紀錄分享一下怎麼使用硬體加密的 SSH 和 PGP 簽章。&lt;&#x2F;p&gt;
&lt;p&gt;Yubikey 的原理是它有一個硬體的「安全元件 (secure element)」可以用來實做各種智慧卡 (smart card) 的功能，這裡我們關心的是它的 PIV (personal identity verification) 與 PGP 兩個智慧卡界面。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;piv-yu-pkcs-11-yu-pc-sc&quot;&gt;PIV 與 PKCS#11 與 PC&#x2F;SC&lt;&#x2F;h2&gt;
&lt;p&gt;PIV 是一種智慧卡的標準，定義了智慧卡需要支援的金鑰格式，演算法等等。Yubikey 的 PIV 支援標準的 9a, 9c, 9d, 9e 金鑰儲區，還有額外的 10 個以上的過期金鑰儲存位置。這些標準的金鑰儲存區分別用來「認證」、「簽章」、「加密」、「驗證」。雖然沒有強制規定這些金鑰要怎麼使用，通常 SSH 會設定為使用 9a 的「認證」用金鑰區來儲存 SSH 的私鑰。&lt;&#x2F;p&gt;
&lt;p&gt;SSH 不是直接使用 PIV 來使用智慧卡，而是支援另外一個標準 PKCS#11，這個標準定義了統一的抽象軟體界面來提供硬體的加密演算法功能。Yubico 有提供自己的 libykcs11.so 這個函式庫可以直接使用 Yubikey 上的 PIV 功能，而 Fedora, Debian 等系統則預設使用 OpenSC 的 opensc-pkcs11.so 這個函式庫。OpenSC 則仰賴 pcscd-lite 這個程式透過標準的 PC&#x2F;SC 界面和各種不同的智慧卡溝通。&lt;&#x2F;p&gt;
&lt;p&gt;因為同一個系統上可以同時有多個不同的 PKCS#11 函式庫，Fedora 便用 p11-kit 的 p11-kit-proxy 來更簡單的使用不同的 PKCS#11 提供元。&lt;&#x2F;p&gt;
&lt;p&gt;PKCS#11 相關的軟體生態相當複雜，簡化為一張圖的話大概繪像這樣：&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;fedora-pkcs11-ssh-and-pgp&#x2F;pkcs11-2023-03-05-1621-1.png&quot;
      alt=&quot;PKCS#11 生態練&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; PKCS#11 生態練
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;實際上因為用了 p11-kit 的關係，所以設定起來其實非常簡單。首先先用 ykman 產生私鑰(預設是 RSA 2048 bit)跟公鑰(包裝為 X.509 憑證格式)：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; dnf&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; yubikey-manager&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ykman&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; piv&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; keys&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; generate&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 9a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; public.pem&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ykman&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; piv&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; certificates&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; generate&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; CN=SSH&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 9a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; public.pem&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然後就可以使用了，可以用 ssh-keygen 把公鑰弄出來，然後設定 ssh 預設用 p11-kit-proxy 來存取私鑰：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ssh-keygen&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;D&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; pkcs11:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; tee&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ~&#x2F;.ssh&#x2F;id_rsa.pub&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;ssh-rsa&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; AAAAB3NzaC1yc2EAAAADAQABAAABAQC0tEUNb5QGKlGW8wcv+WYZCN1xKZE7NLFeCqj8QUbsfuobudTp8fZYCwtF8UwkwXpVTTg5RLdFOanp0V3ZDDVanyZEzopfhghcmZAmNJ5UoBZx+hOerlTHBmZNBJY0T8MzogQO60UluVYqOwcRynQfdV+F2oNrhqNph0BHIOgPTtlc5WO6R6VXcFBaHQROBDvB7hAi1MrIqZ5AoX9bI7thYbmzQuK6PYbFQEcSXrxyvnb4Xus7hmk6Vw7itn65&#x2F;7JMKhgAz3jRR0HHAyRBB2pjqPxdNFOMDB3IieviiYOGvMlfZ2HaimQB8c6EvC8i+G3&#x2F;KV&#x2F;w5C8zP0VE+lfGhyHn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; PIV&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; AUTH&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; pubkey&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; pkcs11:id=%01&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;P&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;I&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;V&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;A&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;U&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;H&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;u&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;k&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;y&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;S&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;H&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt;manufacturer&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;_&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;I&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;I&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;module-path&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;u&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;l&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;6&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;k&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;o&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;y&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;o&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;IdentityFile &amp;quot;pkcs11:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ~&#x2F;.ssh&#x2F;config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; 也可以把私鑰加到 ssh-agent 中&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ssh-add&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; pkcs11:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;設定起來算是相當無腦(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fedoramagazine.org&#x2F;fedora-28-better-smart-card-support-openssh&#x2F;&quot;&gt;參考文章&lt;&#x2F;a&gt;)。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gnupg-yu-scdaemon&quot;&gt;GnuPG 與 scdaemon&lt;&#x2F;h2&gt;
&lt;p&gt;我一開始用 Yubikey 主要就是為了它的硬體 PGP 智慧卡，搭配 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gnupg.org&#x2F;&quot;&gt;GnuPG&lt;&#x2F;a&gt; 可以實現簽章、加密、SSH 認證三個功能。這是一個常用的功能，而且網路上已經有許多的文件說明，這裡就不再重複。我想要紀錄的是如何讓 GnuPG 與 PIV 共存。&lt;&#x2F;p&gt;
&lt;p&gt;網路搜尋可以發現許多關於 gpg –card-status 不正常運作的問題：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; gpg&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-card-status&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; selecting&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; openpgp&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; failed:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; No&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; such&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; device&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; OpenPGP&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; card&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; not&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; available:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; No&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; such&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; device&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;這是因為 GnuPG 跟 OpenSC &#x2F; pcscd 都需要透過 CCID 這個 USB 通訊協定跟智慧卡溝通，但是 GnuPG 用來實現這個功能的工具程式 scdaemon 預設會開啟獨占模式，不允許其他的程式也使用系統上的智慧卡，理由是 scdaemon 會暫時快取一些狀態在記憶體裡面，如果其他程式也同時使用智慧卡的話，有可能會造成狀態不一致的錯誤。相關的討論中如 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dev.gnupg.org&#x2F;T6218&quot;&gt;T6218&lt;&#x2F;a&gt; 和 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dev.gnupg.org&#x2F;T2440&quot;&gt;T2440&lt;&#x2F;a&gt; 可以看到 GnuPG 的開發者並不願意改變這個預設行為。&lt;&#x2F;p&gt;
&lt;p&gt;網路上有許多很久以前的解法，甚至 pcsc-lite 的作者都有一篇 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ludovicrousseau.blogspot.com&#x2F;2019&#x2F;06&#x2F;gnupg-and-pcsc-conflicts.html&quot;&gt;blog&lt;&#x2F;a&gt; 說明這個問題。我測試後現在可以暫時解決這個問題的方法是強制 scdaemon 以共享模式使用 pscsd 的資源：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; $HOME&#x2F;.gnupg&#x2F;scdaemon.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;disable-ccid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;pcsc-shared&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;這樣就可以讓 gpg 幾乎都可以正常運作。如果還是有問題，跑指令&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; gpgconf&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-kill&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;也幾乎都可以解決。&lt;&#x2F;p&gt;
&lt;p&gt;基於現在 Debian 還是只能用 PGP 來認證上傳的套件，希望 GnuPG 可以改善 scdaemon 的預設行為，跟其他軟體和平相處。▞&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>用 podman-kube 來跑 WordPress</title>
        <published>2023-11-18T00:00:00+00:00</published>
        <updated>2023-11-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/podman-kube-wordpress/"/>
        <id>https://kanru.info/podman-kube-wordpress/</id>
        
        <content type="html" xml:base="https://kanru.info/podman-kube-wordpress/">
            &lt;img src="https://kanru.info/processed_images/z_featured.ac6513186736cf81.jpg" class="featured" /&gt;&lt;p&gt;上一篇寫了要怎麼用 podman 的 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-systemd.unit.5.html&quot;&gt;systemd generator&lt;&#x2F;a&gt; 來跑單一的 container，其實 podman 還有另外一個 pod 模式，也就是類似 Kubernetes 的 pod 一樣，可以把多個 container 跑在同一個 namespace 裡面。Podman 的 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-kube-play.1.html&quot;&gt;podman-kube-play(1)&lt;&#x2F;a&gt; 指令可以接受一個 Kubernetes 的 YAML 設定檔然後跑起單一個 container 或是一整個 pod 與多個 container。同樣的，systemd generator 也可以用來管理整個 pod，變成 systemd service unit。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;podman-kube-wordpress&#x2F;wordpress-container-2023-03-05-1621.png&quot;
      alt=&quot;原本的架構圖&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 原本的架構圖
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;這次我要做的是把單一的 WordPress container 改成用 PHP-fpm 和 Caddy 兩個 container 的方式佈署。原本單一的 WordPress container 用的是 apache2 prefork mpm，同時處理多個 HTTP request 效率比較不好，換成兩個獨立的 container 後，可以把 PHP 與靜態檔案分別交給不同的 process 處理，這樣各自效率都比較好。當然也不一定要用 Caddy，其他像是 nginx 或是 apache2 event mpm 也可以。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;podman-kube-wordpress&#x2F;wordpress-pod-2023-03-05-1621-1024x331.png&quot;
      alt=&quot;新的架構圖&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 新的架構圖
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Podman 的改版還滿快的，新的版本常常都會新增許多功能。這次我使用的是 Podman 4.7.0 版。因為我記不住要怎麼寫 Kubernetes 的 YAML，所以我用比較簡單的方法：先用指令產生好 pod 跟 container 後，再用 podman kube generate 來產生 YAML.&lt;&#x2F;p&gt;
&lt;p&gt;首先創造一個空的 pod 來：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; pod&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; create&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-network&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; core&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress-pod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Network 可以隨意設定，之後可以再改。接下來可以創造 File Server container：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; create&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-pod&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress-pod&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress-php&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;        -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;var&#x2F;srv&#x2F;wordpress:&#x2F;var&#x2F;www&#x2F;html:Z&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;        -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; WORDPRESS_DB_HOST=wp-db&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;        wordpress:fpm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最後，用 podman-kube 來產生 Kubernetes 的 YAML 設定檔：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; kube&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; generate&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress-pod&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress-pod.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;產生出來的設定類似這樣：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Save the output of this file and use kubectl create -f to import&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; it into Kubernetes.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Created with podman-4.7.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;piVersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;k&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; P&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;od&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;etadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;  a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;nnotations&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ind-mount-options&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;var&#x2F;srv&#x2F;wordpress:Z&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ind-mount-options&#x2F;wordpress-pod-wordpress-fs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;var&#x2F;srv&#x2F;wordpress:Z&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ind-mount-options&#x2F;wordpress-pod-wordpress-php&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;var&#x2F;srv&#x2F;wordpress:Z&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;  c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;reationTimestamp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;2023-11-18T07:10:21Z&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;  l&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;abels&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;pp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ordpress-pod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;  n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ordpress-pod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;pec&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;  c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ontainers&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ocker.io&#x2F;library&#x2F;caddy:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ordpress-pod-wordpress-fs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;olumeMounts&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ountPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;etc&#x2F;caddy&#x2F;Caddyfile&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;tc-caddy-wordpress-fs-host-0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ountPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;var&#x2F;www&#x2F;html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ar-srv-wordpress-host-1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ountPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ordpress-fs-data-pvc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;rgs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;hp-fpm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;nv&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; W&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ORDPRESS_DB_HOST&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;alue&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;p-db&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ocker.io&#x2F;library&#x2F;wordpress:fpm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ordpresss-pod-wordpress-php&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;olumeMounts&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ountPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;var&#x2F;www&#x2F;html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ar-srv-wordpress-host-0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;  r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;estartPolicy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; A&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;  v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;olumes&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ostPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;etc&#x2F;caddy&#x2F;wordpress-fs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      t&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ype&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; F&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ile&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;tc-caddy-wordpress-fs-host-0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ostPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;var&#x2F;srv&#x2F;wordpress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      t&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ype&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; D&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;irectory&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ar-srv-wordpress-host-1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ordpress-fs-data-pvc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ersistentVolumeClaim&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;laimName&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ordpreess-fs-data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt; h&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ostPath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;var&#x2F;srv&#x2F;wordpress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;      t&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ype&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; D&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;irectory&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ar-srv-wordpress-host-0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最後就可以用 podman-kube-play 來測試：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; kube&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; play&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-network&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; core&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress-pod.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;滿意了就可以把設定弄成 systemd unit&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; &#x2F;etc&#x2F;containers&#x2F;systemd&#x2F;wordpress-pod.kube&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Unit&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;WordPress website&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;After&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;network-online.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Wants&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;network-online.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Kube&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Network&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;core&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Yaml&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;etc&#x2F;containers&#x2F;systemd&#x2F;wordpress-pod.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Restart&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;always&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;TimeoutStartSec&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;900&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Install&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;WantedBy&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;multi-user.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;整體而言算是相當簡單就可以設定好一個 pod，再加一些設定還可以做動自動更新或是自動佈署最新的設定，也可以把同樣的設定檔改一改就拿去大型的 Kubernetes 叢集跑。▗&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;參考文章：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;caddy.community&#x2F;t&#x2F;setting-up-wordpress-with-caddy-on-ubuntu&#x2F;18448&quot;&gt;https:&#x2F;&#x2F;caddy.community&#x2F;t&#x2F;setting-up-wordpress-with-caddy-on-ubuntu&#x2F;18448&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-systemd.unit.5.html#name&quot;&gt;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-systemd.unit.5.html#name&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-kube-play.1.html&quot;&gt;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-kube-play.1.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>用 systemd 管理 podman containers</title>
        <published>2023-11-06T00:00:00+00:00</published>
        <updated>2023-11-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/podman-systemd-unit/"/>
        <id>https://kanru.info/podman-systemd-unit/</id>
        
        <content type="html" xml:base="https://kanru.info/podman-systemd-unit/">
            &lt;img src="https://kanru.info/processed_images/z_featured.b9beeded8c93bb70.jpg" class="featured" /&gt;&lt;p&gt;Podman 原本有提供指令可以產生 systemd service 的設定檔，但是產生出來的檔案內容感覺有很多改善的空間，例如要怎麼讓 podman 可以乖乖的把 log 送到 journald 就不是產生的範本有提供的。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;podman&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; generate&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; systemd&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress-container&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在研究這個問題的時候發現這個指令已經被 Quadlet 取代了。Quadlet 是在 podman 4.4 的時候整合進去的，利用 systemd generator 的方式把類似於 systemd 的設定檔直接轉換為 systemd 可以使用的格式。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; cat &#x2F;etc&#x2F;containers&#x2F;systemd&#x2F;wordpress.container&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Unit&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;Wordpress Container&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;After&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;network-online.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Wants&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;network-online.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Container&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Image&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;docker.io&#x2F;library&#x2F;wordpress:6&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Volume&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;var&#x2F;srv&#x2F;wordpress:&#x2F;var&#x2F;www&#x2F;html:Z&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;Restart&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;always&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;TimeoutStartSec&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;900&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Install&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;WantedBy&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;multi-user.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如此一來就可以簡單的設定 container 以及讓 container 自動更新了。詳細的設定可以參考 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-systemd.unit.5.html&quot;&gt;podman-systemd.unit(5)&lt;&#x2F;a&gt; 的內容。&lt;&#x2F;p&gt;
&lt;p&gt;把設定放到 &#x2F;etc&#x2F;containers&#x2F;systemd 目錄下以後，記得要讓 systemd 重新讀取：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; sudo systemctl daemon-reload&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; sudo systemctl start wordpress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; systemctl status wordpress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;●&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; wordpress.service&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; WordPress&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; Container&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;     Loaded:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; loaded&lt;&#x2F;span&gt;&lt;span&gt; (&#x2F;etc&#x2F;containers&#x2F;systemd&#x2F;wordpress.container&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; generated&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Drop-In:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;usr&#x2F;lib&#x2F;systemd&#x2F;system&#x2F;service.d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;             └─10-timeout-abort.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;     Active:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; active&lt;&#x2F;span&gt;&lt;span&gt; (running&lt;&#x2F;span&gt;&lt;span&gt;) since Sun 2023-11-05 03:07:06 UTC&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; 20h&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ago&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   Main&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; PID:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 57888&lt;&#x2F;span&gt;&lt;span&gt; (conmon&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;      Tasks:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt; (limit:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 2032&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;     Memory:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 212.2M&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        CPU:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 4min&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 34.751s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;     CGroup:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;system.slice&#x2F;wordpress.service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;             ├─libpod-payload-739aab67986c96c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;             │&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ├─57890&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; apache2&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;DFOREGROUND&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;             │&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ├─74394&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; apache2&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;DFOREGROUND&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;             │&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ├─74399&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; apache2&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;DFOREGROUND&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;             │&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; └─74407&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; apache2&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;DFOREGROUND&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;             └─runtime&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;               └─57888&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;usr&#x2F;bin&#x2F;conmon&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-api-version&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 739aab67986c96c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;這一種設定方式比原本的 systemd 簡單太多了。▗&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>Fedora Silverblue 新增硬碟到 Btrfs 陣列</title>
        <published>2023-05-16T00:00:00+00:00</published>
        <updated>2023-05-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/fedora-silverblue-add-btrfs-raid/"/>
        <id>https://kanru.info/fedora-silverblue-add-btrfs-raid/</id>
        
        <content type="html" xml:base="https://kanru.info/fedora-silverblue-add-btrfs-raid/">
            &lt;img src="https://kanru.info/processed_images/z_featured.32b68cb65910a572.jpg" class="featured" /&gt;&lt;p&gt;我目前的主力電腦是一台 2020 年購入的 System76 的 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tech-docs.system76.com&#x2F;models&#x2F;lemp9&#x2F;README.html&quot;&gt;Lemur Pro (lemp9)&lt;&#x2F;a&gt;，前陣子無線網路卡掛點，自己換了新的 Intel AX210 模組。&lt;&#x2F;p&gt;
&lt;p&gt;今天有感於最初設定的規格 500 GB 的 SSD 儲存空間有點不夠用了，想要加裝新的 SSD 來擴增容量。lemp9 內部有兩個 M.2 NVMe 的空間，所以我還有一個空位可以用。&lt;&#x2F;p&gt;
&lt;p&gt;原本內裝的是 Samsung 的 970 EVO Plus 500 GB，所以這次就添購了同樣是 Samsung 的 970 EVO Plus 2 TB，一次加好加滿。&lt;&#x2F;p&gt;
&lt;p&gt;我大約半年前把主力作業系統換成 Fedora 並且裝了 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;silverblue.fedoraproject.org&#x2F;&quot;&gt;Fedora Silverblue&lt;&#x2F;a&gt; 這個版本，是一種新型的 immutable 作業系統安裝方式，安裝基底系統後，額外的軟體都是用 Flatpak 或是 container 的形式疊加上去的。&lt;&#x2F;p&gt;
&lt;p&gt;有如其他 Fedora Workstation 系統，Silverblue 已經預設使用 Btrfs 為主要檔案系統。這次我要新增一個 SSD 並且加到硬碟陣列中。主要的一個問題是我有用 LUKS 形式的全硬碟加密，增加了新的 SSD 之後要改些設定讓他在開機的時候會自動解鎖。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang-xin-de-ssd&quot;&gt;安裝新的 SSD&lt;&#x2F;h2&gt;
&lt;p&gt;按照 System76 網站上的&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tech-docs.system76.com&#x2F;models&#x2F;lemp9&#x2F;repairs.html#replacing-an-m2nvme-ssd&quot;&gt;維修手冊&lt;&#x2F;a&gt;就可以輕鬆安裝。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ge-shi-hua-xin-de-ssd&quot;&gt;格式化新的 SSD&lt;&#x2F;h2&gt;
&lt;p&gt;使用 GNOME 內建的 Disks 工具軟體，幫新的 SSD 建立 GPT 分割表，然後產生一個分割區，不要建立檔案系統但是要加密。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;fedora-silverblue-add-btrfs-raid&#x2F;z_screenshot.png&quot;
      alt=&quot;Disks 管理工具&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; Disks 管理工具
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;xin-zeng-dao-btrfs-zhen-lie&quot;&gt;新增到 Btrfs 陣列&lt;&#x2F;h2&gt;
&lt;p&gt;參考這&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.tnonline.net&#x2F;w&#x2F;Btrfs&#x2F;Adding_and_removing_devices&quot;&gt;篇詳細的說明&lt;&#x2F;a&gt;，首先把解鎖後的裝置加入&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; btrfs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; device&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; add&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;dev&#x2F;mapper&#x2F;luks-d56dd585-059c-45b7-8eab-4e1df1e3d769&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;mnt&#x2F;btr_pool&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然後把 metadata 轉成 RAID1&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; btrfs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; balance&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; start&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;mconvert=raid1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;mnt&#x2F;btr_pool&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;接下來就可以確定新增完成：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; btrfs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; fi&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; usage&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Overall:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Device&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; size:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;		   2.27TiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Device&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; allocated:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;		 432.07GiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Device&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; unallocated:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;		   1.85TiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Device&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; missing:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;		     0.00B&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Device&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; slack:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;		     0.00B&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Used:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;			 287.40GiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Free&lt;&#x2F;span&gt;&lt;span&gt; (estimated&lt;&#x2F;span&gt;&lt;span&gt;):		   1.99TiB	(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;min:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 1.06TiB&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Free&lt;&#x2F;span&gt;&lt;span&gt; (statfs,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; df&lt;&#x2F;span&gt;&lt;span&gt;):		   1.99TiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Data&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ratio:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;			      1.00&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Metadata&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; ratio:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;		      2.00&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Global&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; reserve:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;		 512.00MiB&lt;&#x2F;span&gt;&lt;span&gt;	(used:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; 0.00B&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Multiple&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; profiles:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;		        no&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Data,single:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; Size:416.01GiB,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; Used:274.19GiB&lt;&#x2F;span&gt;&lt;span&gt; (65.91%&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   &#x2F;dev&#x2F;mapper&#x2F;luks-4845f8f9-cfdf-4488-9611-68a7789869dd&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;	 416.01GiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Metadata,RAID1:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; Size:8.00GiB,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; Used:6.60GiB&lt;&#x2F;span&gt;&lt;span&gt; (82.56%&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   &#x2F;dev&#x2F;mapper&#x2F;luks-4845f8f9-cfdf-4488-9611-68a7789869dd&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;	   8.00GiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   &#x2F;dev&#x2F;mapper&#x2F;luks-d56dd585-059c-45b7-8eab-4e1df1e3d769&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;	   8.00GiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;System,RAID1:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; Size:32.00MiB,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; Used:80.00KiB&lt;&#x2F;span&gt;&lt;span&gt; (0.24%&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   &#x2F;dev&#x2F;mapper&#x2F;luks-4845f8f9-cfdf-4488-9611-68a7789869dd&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;	  32.00MiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   &#x2F;dev&#x2F;mapper&#x2F;luks-d56dd585-059c-45b7-8eab-4e1df1e3d769&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;	  32.00MiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Unallocated:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   &#x2F;dev&#x2F;mapper&#x2F;luks-4845f8f9-cfdf-4488-9611-68a7789869dd&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;	  40.12GiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;   &#x2F;dev&#x2F;mapper&#x2F;luks-d56dd585-059c-45b7-8eab-4e1df1e3d769&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;	   1.81TiB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;xiu-gai-kai-ji-can-shu&quot;&gt;修改開機參數&lt;&#x2F;h2&gt;
&lt;p&gt;為了要在開機的時候自動解鎖兩個 SSD 一定要記得修改開機參數，不然開機後只有一個硬碟解鎖，會卡在開機掛載系統分割區。&lt;&#x2F;p&gt;
&lt;p&gt;在 Silverblue 可以用 rpm-ostree 指令管理開機參數：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;rpm-ostree&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; kargs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;-append=rd.luks.uuid=luks-d56dd585-059c-45b7-8eab-4e1df1e3d769&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;重新開機後就可以驗證宜一切都設定好了！▗&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>2022 十月箱根旅行</title>
        <published>2023-02-05T00:00:00+00:00</published>
        <updated>2023-02-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/2022-hakone-travel/"/>
        <id>https://kanru.info/2022-hakone-travel/</id>
        
        <content type="html" xml:base="https://kanru.info/2022-hakone-travel/">
            &lt;img src="https://kanru.info/processed_images/z_featured.70e54677243a7b85.jpg" class="featured" /&gt;&lt;p&gt;試著發表一些旅行時拍的照片！&lt;&#x2F;p&gt;
&lt;p&gt;相機 Fujifilm X-Pro2，鏡頭 XF35mmF1.4R，後製 Darktable 4.0&lt;&#x2F;p&gt;

&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0001-jpg.webp&quot;
        alt=&quot;在小田原駅等車前往箱根湯本&quot;width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;figcaption&gt;
        &amp;#x25B4; 在小田原駅等車前往箱根湯本
    &lt;&#x2F;figcaption&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0005-jpg.webp&quot;
        alt=&quot;搭乘登山小火車&quot;width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;figcaption&gt;
        &amp;#x25B4; 搭乘登山小火車
    &lt;&#x2F;figcaption&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0006-jpg.webp&quot;
        alt=&quot;搭乘空中纜車到大湧谷&quot;width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;figcaption&gt;
        &amp;#x25B4; 搭乘空中纜車到大湧谷
    &lt;&#x2F;figcaption&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0008-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0009-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0014-jpg.webp&quot;
        alt=&quot;大湧谷的火山地形&quot;width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;figcaption&gt;
        &amp;#x25B4; 大湧谷的火山地形
    &lt;&#x2F;figcaption&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0017-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0018-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0020-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0026-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0027-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0029-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0030-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0036-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0047-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0052-jpg.webp&quot;
        alt=&quot;經由蘆之湖坐巴士到仙石原すすき高原&quot;width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;figcaption&gt;
        &amp;#x25B4; 經由蘆之湖坐巴士到仙石原すすき高原
    &lt;&#x2F;figcaption&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0055-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0070-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0072-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0073-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0075-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0117-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0120-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0124-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0128-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0131-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0136-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0163-jpg.webp&quot;
        alt=&quot;箱根湿生花園&quot;width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;figcaption&gt;
        &amp;#x25B4; 箱根湿生花園
    &lt;&#x2F;figcaption&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0164-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0165-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0170-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0172-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0173-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0175-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0176-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0178-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0179-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0181-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0186-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0187-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0191-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0196-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0211-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0213-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0214-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0215-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0221-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0222-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0225-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0229-jpg.webp&quot;
        alt=&quot;箱根神社&quot;width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;figcaption&gt;
        &amp;#x25B4; 箱根神社
    &lt;&#x2F;figcaption&gt;
    &lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
    &lt;picture&gt;
        &lt;img
        class=&quot;gallery-image__image&quot;
        src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;2022-hakone-travel&#x2F;20221017_0230-jpg.webp&quot;
        width=&quot;900&quot;
        loading=&quot;lazy&quot;&gt;
    &lt;&#x2F;picture&gt;
    &lt;&#x2F;figure&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>加入 Fediverse 聯邦宇宙</title>
        <published>2022-11-04T00:00:00+00:00</published>
        <updated>2022-11-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/join-fediverse/"/>
        <id>https://kanru.info/join-fediverse/</id>
        
        <content type="html" xml:base="https://kanru.info/join-fediverse/">
            &lt;img src="https://kanru.info/processed_images/z_featured.9d98e5da2ae51ded.jpg" class="featured" /&gt;&lt;p&gt;最近因為 Twitter 被收購，許多人開始找替代的服務，所以 Mastodon 又開始有很多新人加入。和一般社交網路不一樣，Mastodon 是建立在一個無單一主站的架構上面。而上一篇我換回到 WordPress 其實跟這個還有點關係 😉&lt;&#x2F;p&gt;
&lt;p&gt;這個由架構所建立起來的社交網路又稱作 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.m.wikipedia.org&#x2F;wiki&#x2F;Fediverse&quot;&gt;Fediverse&lt;&#x2F;a&gt;，裡面並不只是 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;joinmastodon.org&#x2F;&quot;&gt;Mastodon&lt;&#x2F;a&gt;，只要是使用 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.m.wikipedia.org&#x2F;wiki&#x2F;ActivityPub&quot;&gt;ActivityPub&lt;&#x2F;a&gt; 這個協議的網站都有機會串接起來！現在已經有各種類似 Twitter 的微型部落格，或是類似 Instagram 的照片分享平台，都可以互相串連。而 WriteFreely 則是介於傳統部落格與微型部落格之間。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;join-fediverse&#x2F;z_10892b531117e0ff.png&quot;
      alt=&quot;&amp;lt;a href=&amp;quot;https:&amp;#x2F;&amp;#x2F;axbom.com&amp;#x2F;fediverse&amp;#x2F;&amp;quot;&amp;gt;世界樹&amp;lt;&amp;#x2F;a&amp;gt;&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; &amp;lt;a href=&amp;quot;https:&amp;#x2F;&amp;#x2F;axbom.com&amp;#x2F;fediverse&amp;#x2F;&amp;quot;&amp;gt;世界樹&amp;lt;&amp;#x2F;a&amp;gt;
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;而 WordPress 上有人做了一個 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wordpress.org&#x2F;plugins&#x2F;activitypub&#x2F;&quot;&gt;ActivityPub 的 plugin&lt;&#x2F;a&gt; 可以把部落格網站直接變成一個 Fediverse 上面的節點，可以直接被 Mastodon 或是其他網站跟隨。比如說可以在 Mastodon 裡面搜尋 @kanru 就會找到我！更有趣的是回文會變成網站上面的留言，所以互動是雙向的。&lt;&#x2F;p&gt;
&lt;p&gt;讓我覺得，如果說 Web2.0 是創作者與讀者互動網站，早期部落格的 RSS 訂閱，跨網站的引用通知 (Ping back &amp;amp; Trackbacks) 是長篇文章的互動的話，ActivityPub 所促成的正是短篇文章 (microblog) 的互動，可不可以算是 Web3.0？最少也是 Web2.0 的文藝復興了。▗&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>回頭用 WordPress 寫部落格</title>
        <published>2022-11-03T00:00:00+00:00</published>
        <updated>2022-11-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/returning-to-wordpress/"/>
        <id>https://kanru.info/returning-to-wordpress/</id>
        
        <content type="html" xml:base="https://kanru.info/returning-to-wordpress/">
            &lt;img src="https://kanru.info/processed_images/z_featured.c06c1faf869e69ce.jpg" class="featured" /&gt;&lt;p&gt;網路上有一個笑話：有在寫部落格的工程師，都至少有寫過一次自己的靜態網頁編譯軟體，而且比起真正寫部落格，會花更多時間在重構這個軟體或是調整模板。回頭看看我自己網站上面的文章，自從發出 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog-redesigned&#x2F;&quot;&gt;Blog Redesigned&lt;&#x2F;a&gt; 這篇文章之後，文章數量從每年幾篇，直接掉到每三年一篇。為什麼呢？&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;returning-to-wordpress&#x2F;z_screenshot.png&quot;
      alt=&quot;2010 年之後就幾乎沒有幾個月有新文章&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2010 年之後就幾乎沒有幾個月有新文章
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;這幾年我換過好幾次靜態網頁產生軟體，調整過好幾次網站設計，換過各種服務平台（雲端虛擬主機、Vercel、Cloudflare、Cloudfront+S3、Lambda、雲端虛擬主機），但是文章數量總共增加了兩篇，免強加上翻譯為英文跟日文，也才只有五篇。&lt;&#x2F;p&gt;
&lt;p&gt;去年初幫老婆的姊姊架了一個新的 WordPress 網站，主要讓她寫一些小品心得文章還有讓她有可以宣傳她自己的產品的網站。過程中還開發了一個使用 Cloudflare Zero Trust 的外掛，架了幾個測試站，才發現 WordPress 已經改變進步了很多。&lt;&#x2F;p&gt;
&lt;p&gt;之後我又陸續測試了 Ghost 跟 WriteFreely ，了解到重點不在於靜態編譯的網頁可以有多快的速度、多省資源，而是一個環境會不會讓你有可以維持繼續的寫下去的動力。&lt;&#x2F;p&gt;
&lt;p&gt;WordPress 有兩個我覺得很有用的功能，第一個是最新的古騰堡編全站輯器有區塊的概念，可以很簡單的加入圖片並且調整圖片的樣式是滿版還是中間對齊，也可以針對不同文章給予不同的排版，第二點是可以把文章存到草稿，然後很快的打開就可以繼續編輯。這兩個功能讓「寫文章」這件事的阻力變小，也就比較沒有心裡負擔可以輕鬆的寫。以前換到靜態網頁編譯器之後，光是要開始寫一篇新文章，就要先花點時間研究文章標題等等後設資料(front matter)要怎麼設定，等到搞定之後，已經沒有力氣寫本文了。&lt;&#x2F;p&gt;
&lt;p&gt;趁著這週 WordPress 6.1 釋出，把所有文章都搬過來了，希望這次搬回 WordPress 之後可以多發表一些技術還有隨手拍的文章！▗&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>桔梗種植的第二年更新</title>
        <published>2021-07-16T00:00:00+00:00</published>
        <updated>2021-07-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/platycodon-2/"/>
        <id>https://kanru.info/platycodon-2/</id>
        
        <content type="html" xml:base="https://kanru.info/platycodon-2/">&lt;p&gt;桔梗的葉子在冬天會枯萎，但是地下長的像人蔘的根部則是會進入休眠狀態。等到春天白天氣溫約 17 度的時候，就會重新萌發出新芽。&lt;&#x2F;p&gt;
&lt;p&gt;我種在陽台的桔梗在去年過冬前就已經枯的七七八八，完全枯死那種。剩下的最後一棵頑強的桔梗，在氣溫進入 10 度以下後也似乎漸漸進入休眠，只是實在難以判斷是否還有機會重新萌發。就這樣半信半疑，每隔一段時間仍然給盆栽水分，讓土壤保持微微溼潤。&lt;&#x2F;p&gt;
&lt;p&gt;今年春天，三月初的時候，從桔梗開始休眠已經過了三、四個月。有一天我仍然例行檢查盆栽，驚喜的發現桔梗的枯莖旁邊冒出了一個小小的像筍子的東西，隔幾天又多冒出兩個筍子。根據它們的位置看起來，它們應該都是去年最後一棵桔梗重新萌發的。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0317_070922647.jpg&quot;
      alt=&quot;桔梗新芽&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-03-17 桔梗新芽冒出
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;第二年重新長出來的桔梗和新芽很不一樣，粗壯好幾倍，生長速度也快非常多。每天可以長高一兩公分，等到三月底的時候已經可以看出來是三棵強壯的桔梗了。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0320_10525300.jpg&quot;
      alt=&quot;桔梗新芽越長越高&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-03-20 桔梗新芽越長越高
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0323_000608225.jpg&quot;
      alt=&quot;桔梗新芽有如蘆筍&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-03-23 桔梗新芽有如蘆筍
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0331_063119291.jpg&quot;
      alt=&quot;桔梗新芽葉子開始展開&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-03-31 桔梗新芽的葉子開始展開
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;四月中，所有的葉子都已經展開，長到大約 15 公分左右。之後，因為我臨時必須回台灣一趟，所以給它們設置了自動澆水的裝置，中間約有一個月多的時間無法紀錄，只能希望它們可以撐到我回來。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0406_233235529.jpg&quot;
      alt=&quot;桔梗新芽有15公分高&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-04-06 桔梗新芽已經有 15 公分高
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0411_014458687.jpg&quot;
      alt=&quot;桔梗新芽葉子健康飽滿&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-04-11 桔梗新芽出國前的樣子
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;等到我從台灣回來之後看到的是雖然沒有枯掉，但是長的東倒西歪的桔梗，真是哭笑不得。原本以為粗壯幾倍的桔梗應該可以長的比較挺，不會像第一年一樣伏在地上，結果可能因為沒有修剪加上今年春天陽光比較不夠，最後還是長到歪了。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0525_091304243.jpg&quot;
      alt=&quot;東倒西歪的桔梗&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-05-25 東倒西歪的桔梗
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;其中有有一棵看起來是被強風吹斷了，已經半枯，就乾脆剪到底座。其他兩棵也因為長的不是很好，被第一年就有的真菌感染，葉子上許多白點。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0605_001823678.jpg&quot;
      alt=&quot;桔梗生病的葉子&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-06-05 桔梗生病的葉子
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;六月中，雖然葉子還是不健康，花苞還是陸續結出來了。和第一年不一樣的是，花苞非常的多，最後最高紀錄同時開了九朵呢！&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0609_18063200.jpg&quot;
      alt=&quot;桔梗的花苞&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-06-09 桔梗的花苞
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0613_09301400.jpg&quot;
      alt=&quot;桔梗開花&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-06-13 桔梗開花
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon-2&#x2F;2021_0622_054123906.jpg&quot;
      alt=&quot;桔梗開滿花&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 2021-06-22 桔梗開滿花
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>桔梗花的種植與照顧</title>
        <published>2020-08-08T00:00:00+00:00</published>
        <updated>2020-08-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/platycodon/"/>
        <id>https://kanru.info/platycodon/</id>
        
        <content type="html" xml:base="https://kanru.info/platycodon/">&lt;p&gt;桔梗（學名：Platycodon grandiflorus），别名包袱花、鈴鐺花，擁有可愛的小花，帶有獨特的紙張質地和可見的花脈。我所在地區（日本東京）的常見品種有紫色和白色的花朵。這種植物的一個顯著特徵是它的花苞在完全打開之前像氣球一樣膨脹。&lt;&#x2F;p&gt;
&lt;p&gt;在超市或是居家賣場不難發現它們的身影。當我第一次發現它們之後我就決定要在我的陽台花園種個幾朵。&lt;&#x2F;p&gt;
&lt;p&gt;我在日本亞馬遜買了花的種子以後，用了我原本計畫要種些蔬菜的花盆來種。之後發現這種花盆不是最好的選擇，因為很難控制澆水的水量。之後會詳細解釋。&lt;&#x2F;p&gt;
&lt;p&gt;網路上有許多關於桔梗的資訊，但是關於它們在各個生長期的照片，還有如何照顧它們的文章非常的少。希望我的經驗分享可以對其他想要種桔梗的人有幫助！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-geng-zai-sheng-chang-de-chu-qi-chang-de-fei-chang-huan-man&quot;&gt;桔梗在生長的初期長的非常緩慢&lt;&#x2F;h2&gt;
&lt;p&gt;作為參考，東京的柯本氣候分類法分類裡面是「Cfa」，意思是溫暖帶濕地型夏季炎熱氣候。夏天最熱的時候可以高於 34 攝氏度。冬天的時候溫度最低可以低於零度，甚至有可能下雪。我買到的品種是較嬌小的「桔梗 五月雨 混和」，原生於北海道，所以極端的氣候對它們應該不構成問題。理論上冬天的時候它們會休眠然後隔年春天再重新長芽。根據種子包裝的說明，最好的播種季節是四月或是九月。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20190915_120618_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20190915_120618_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20190915_120618_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20190915_120618_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20190915_120618_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20190915_120618_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20190915_120618_1800.jpg&quot;
      alt=&quot;種下桔梗&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 一百顆桔梗五月雨混和種子都種下了, 2019-09-15
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;我第一次試圖種桔梗是在 2019 年九月。因為它們的發芽需要光照，所以最好把它們放在室外半日照或是有遮蔭的地方。我原本不知道它們在初期長的如此緩慢。大約在播種的二十天之後，種子們慢慢陸續發芽，然後用無比緩慢的速度生長它們的真葉。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191005_100921_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191005_100921_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191005_100921_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191005_100921_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191005_100921_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191005_100921_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191005_100921_1800.jpg&quot;
      alt=&quot;桔梗發芽了&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 播種二十天後的桔梗, 2019-10-05
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;在這個階段我面臨了另外一個問題，也就是要如何幫幼苗澆水。桔梗喜歡排水通暢的帶砂質土壤，所以我必須要維持土讓的濕度又不過度潮濕。問題是它們生長緩慢因此並不太消耗盆栽裡面的水。我所使用的盆栽是設計來保留較多水分的，所以反而是幫倒忙。很難估計多久要澆一次水，每次要澆多少。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191027_091206_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191027_091206_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191027_091206_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191027_091206_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191027_091206_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191027_091206_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_20191027_091206_1800.jpg&quot;
      alt=&quot;桔梗在生長季節&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 接近生長季節的尾聲, 2019-10-27
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;另外一個問題是它們的生長季節不夠長。我猜測它們要儲存足夠的養分長出肥大的肉質根，才有機會在冬天嚴酷的休眠期存活。我懷疑它們一開始葉子會長的如此慢是因為都在長看不到的根部。結果在溫度變低之前，這一批桔梗來不及長到足夠的大小過冬。&lt;&#x2F;p&gt;
&lt;p&gt;冬天的時候桔梗高於地面的部份會枯死。維持耐心的等待，保持土壤溼潤，大約隔年的四月它們就會重新長出嫩芽。是的，大約就在我快要放棄並開始重新播種的時候。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;survivor_20200502_165002_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;survivor_20200502_165002_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;survivor_20200502_165002_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;survivor_20200502_165002_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;survivor_20200502_165002_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;survivor_20200502_165002_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;survivor_20200502_165002_1800.jpg&quot;
      alt=&quot;唯一活過冬天的桔梗&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 唯一活過冬天重新長芽的桔梗, 2020-05-02
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;chun-tian-kai-shi-bo-zhong-rang-shi-qing-jian-dan-xu-duo&quot;&gt;春天開始播種讓事情簡單許多&lt;&#x2F;h2&gt;
&lt;p&gt;我現在知道了，如果我一開始就等到四月才播種，結果也許不會改變太多。儘管它們一開始還是生長的非常緩慢，最終它們會爆發性的開始長葉子。&lt;&#x2F;p&gt;
&lt;p&gt;這次我把它們先種在雞蛋盒裡面，因為這樣子比較容易澆水。我建議使用底部澆水法，因為從上面澆水有可能移動幼苗，干擾它們的根部穩定。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;second_starter_20200421_091752_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;second_starter_20200421_091752_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;second_starter_20200421_091752_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;second_starter_20200421_091752_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;second_starter_20200421_091752_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;second_starter_20200421_091752_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;second_starter_20200421_091752_1800.jpg&quot;
      alt=&quot;從雞蛋盒開始&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 重新開始，這次先種雞蛋盒裡, 2020-04-21
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;春天相對溫暖的氣候似乎有促進幼苗的發展，但是跟隔壁的羅勒比起還是完全不在同一個層次。&lt;&#x2F;p&gt;
&lt;p&gt;它們的根毛開始從盒子的底部冒出，所以是時候把它們換到較大的盆子了。它們不喜歡根部被打擾，所以換盆的時候要小心操作，最好在幼苗長的太大之前進行。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;ready_repot_20200529_070127_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;ready_repot_20200529_070127_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;ready_repot_20200529_070127_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;ready_repot_20200529_070127_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;ready_repot_20200529_070127_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;ready_repot_20200529_070127_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;ready_repot_20200529_070127_1800.jpg&quot;
      alt=&quot;可以換盆了&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 這些桔梗幼苗已經可以換盆了, 2020-05-29
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;換盆之後又過了兩週，它們開始有明顯的成長。原本換盆前就有的兩對真葉長的更大了，全部也都開始長第三對真葉。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;repotted_platycodon_20200614_095731_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;repotted_platycodon_20200614_095731_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;repotted_platycodon_20200614_095731_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;repotted_platycodon_20200614_095731_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;repotted_platycodon_20200614_095731_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;repotted_platycodon_20200614_095731_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;repotted_platycodon_20200614_095731_1800.jpg&quot;
      alt=&quot;換盆後的桔梗&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 換盆後的桔梗長出第三對真葉, 2020-06-14
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;大盆栽的另外一側是空的，因為我原本計畫要再種幾顆幼苗。為什麼呢？因為我本來以為要多種一點才會有足夠的花。畢竟你看，它們看起來就是只會有單一植株，單一朵花的模樣。很多網路上的圖片看起來它們都是叢生的樣子。我錯了！&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;grew_and_grew_20200620_072559_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;grew_and_grew_20200620_072559_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;grew_and_grew_20200620_072559_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;grew_and_grew_20200620_072559_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;grew_and_grew_20200620_072559_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;grew_and_grew_20200620_072559_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;grew_and_grew_20200620_072559_1800.jpg&quot;
      alt=&quot;桔梗越長越大&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 僅僅五天它們就長成這樣了, 2020-06-20
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;jie-geng-dan-ti-ke-yi-chang-chu-duo-ge-ce-ya&quot;&gt;桔梗單體可以長出多個側芽&lt;&#x2F;h2&gt;
&lt;p&gt;換盆的一個月後，它們開始展現它們的主幹會橫臥的傾向。一開始這些主幹會垂直的生長，然後突然開始傾倒往旁邊發展，最後再垂直生長。&lt;&#x2F;p&gt;
&lt;p&gt;它們會朝四面八方生長，如果有人只看每一支幼苗的莖，是很難找到基部在哪裡的。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;decumbent_platycodon_20200711_075742_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;decumbent_platycodon_20200711_075742_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;decumbent_platycodon_20200711_075742_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;decumbent_platycodon_20200711_075742_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;decumbent_platycodon_20200711_075742_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;decumbent_platycodon_20200711_075742_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;decumbent_platycodon_20200711_075742_1800.jpg&quot;
      alt=&quot;桔梗長出橫臥的莖&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 有些桔梗會長出橫臥的莖, 2020-07-11
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;隨著它們越長越大，它們也開始從莖的節點或是基部長出新的分支。我開始理解很多圖片裡面看起來叢生的桔梗，其實很有可能都是同樣的一兩顆桔梗而已。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;new_shoots_2020_0712_12412800_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;new_shoots_2020_0712_12412800_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;new_shoots_2020_0712_12412800_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;new_shoots_2020_0712_12412800_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;new_shoots_2020_0712_12412800_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;new_shoots_2020_0712_12412800_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;new_shoots_2020_0712_12412800_1800.jpg&quot;
      alt=&quot;新分支&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 從桔梗基部長出的新分支, 2020-07-12
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;如果你仔細觀察的話，你可能會發現上面照片裡的葉子有一些小白點。我懷疑這些白點是一種真菌感染的疾病。幸運的是它們似乎沒有造成太大的影響。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-geng-di-yi-nian-jiu-you-ke-neng-kai-hua&quot;&gt;桔梗第一年就有可能開花&lt;&#x2F;h2&gt;
&lt;p&gt;我原本有點擔心我今年可能不會有任何的花，因為它們一開始長的好慢。結果，大約七月底的時候開始陸續有花苞長出來了。每天看著這些「氣球」越長越大我就感到興奮。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-01_07.22.10_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-01_07.22.10_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-01_07.22.10_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-01_07.22.10_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-01_07.22.10_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-01_07.22.10_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-01_07.22.10_1800.jpg&quot;
      alt=&quot;桔梗的花苞&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 多個桔梗的花苞開始出現, 2020-08-01
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;有一天，最大的那一個花苞開始出現些許的紫色。我知道它就快要開花了，但是是什麼時候呢？順帶一提，桔梗對日本人來說是秋天的象徵，看起來這些花還挺準時的。&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-02_07.42.25_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-02_07.42.25_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-02_07.42.25_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-02_07.42.25_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-02_07.42.25_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-02_07.42.25_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;2020-08-02_07.42.25_1800.jpg&quot;
      alt=&quot;桔梗的紫色花苞&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 這個桔梗花苞隨時都會開花, 2020-08-02
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;隔天早上，這朵花就在我還在幫其他植物澆水的時候開了！&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_flower_2020_0802_09551100_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_flower_2020_0802_09551100_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_flower_2020_0802_09551100_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_flower_2020_0802_09551100_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_flower_2020_0802_09551100_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_flower_2020_0802_09551100_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;platycodon_flower_2020_0802_09551100_1800.jpg&quot;
      alt=&quot;桔梗花&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 完全展開的桔梗花, 2020-08-02
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;還有更多的花苞跟桔梗們繼續在生長。希望我今年春天會有更多的桔梗花可以欣賞！&lt;&#x2F;p&gt;
&lt;figure class=&quot;gallery-image&quot;&gt;
  &lt;picture&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;other_flower_buds_2020_0808_09150700_900.avif 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;other_flower_buds_2020_0808_09150700_1800.avif 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;other_flower_buds_2020_0808_09150700_3000.avif 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;avif&quot;&gt;
    &lt;source
      srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;other_flower_buds_2020_0808_09150700_900.webp 900w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;other_flower_buds_2020_0808_09150700_1800.webp 1800w
            , https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;other_flower_buds_2020_0808_09150700_3000.webp 3000w&quot;
      sizes=&quot;900px&quot;
      type=&quot;image&#x2F;webp&quot;&gt;
    &lt;img
      class=&quot;gallery-image__image&quot;
      src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;platycodon&#x2F;other_flower_buds_2020_0808_09150700_1800.jpg&quot;
      alt=&quot;另外一個花苞&quot;
      width=&quot;900&quot;
      loading=&quot;lazy&quot;&gt;
  &lt;&#x2F;picture&gt;
  &lt;figcaption&gt;
    &amp;#x25B4; 另外一個隨時會成熟的花苞, 2020-08-08
  &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;你也種過桔梗嗎？你有沒有任何訣竅想要分享？歡迎寄電子郵件或是在底下留言 :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>JSON Web Tokens 研究筆記</title>
        <published>2020-02-01T00:00:00+00:00</published>
        <updated>2020-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/json-web-tokens/"/>
        <id>https://kanru.info/json-web-tokens/</id>
        
        <content type="html" xml:base="https://kanru.info/json-web-tokens/">&lt;p&gt;JSON Web Tokens 是什麼？它真的可以用來取代傳統的 session cookies 嗎？
本篇為研究 JWT 時所做的筆記。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shen-mo-shi-json-web-tokens&quot;&gt;什麼是 JSON Web Tokens ？&lt;&#x2F;h2&gt;
&lt;p&gt;JSON Web Token 縮寫為 JWT，唸作 jot，是一個 IETF 標準(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tools.ietf.org&#x2F;html&#x2F;rfc7519&quot;&gt;RFC
7519&lt;&#x2F;a&gt;)，定義了不同的群體間可以精
簡且安全的方式交換資訊的方法，這些資訊是包裝在一個 JSON Object 裡面，因
而得名。這些資訊因為經過數位簽章所以可以被驗證是否為可信任的內容，簽章
的方式可以是單一密文(HMAC)或是公鑰私鑰系統(RSA)。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;json-web-tokens-yao-zen-mo-yong&quot;&gt;JSON Web Tokens 要怎麼用？&lt;&#x2F;h2&gt;
&lt;p&gt;當使用者跟伺服器登錄之後，伺服器可以傳回一個 JSON Web Token，此 Token 可
以存在瀏覽器的 local storage 或是 cookie。&lt;&#x2F;p&gt;
&lt;p&gt;當使用者想要再次存取某些需要認證的資訊的時候，他們需要把這個 JWT 送回伺
服器，通常以 Authorization header 的形式，例如：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;這個方式對於伺服器來說相較於 session cookie 的方式輕量(stateless)，因
為伺服器不需要保有相關 session 的訊息，只需要 JWT 內的資訊就可以確認該
使用者是否可以取用相關資源。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;json-web-tokens&#x2F;mermaid-diagram-20200724102650.svg&quot; alt=&quot;Authentication flow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-json-web-tokens-de-hao-chu&quot;&gt;使用 JSON Web Tokens 的好處&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;可以容易的橫向 scale&lt;&#x2F;li&gt;
&lt;li&gt;維護更容易(沒有需要長期儲存的資料)&lt;&#x2F;li&gt;
&lt;li&gt;跨 Domain RESTful API (不需要 CORS cookie)&lt;&#x2F;li&gt;
&lt;li&gt;可以設定 Token 的有效期限&lt;&#x2F;li&gt;
&lt;li&gt;單一 Token 即包含所有認證需要的資訊&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;chuan-tong-de-session&quot;&gt;傳統的 Session&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;json-web-tokens&#x2F;mermaid-diagram-20200724114359.svg&quot; alt=&quot;Session flow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;使用者認證後伺服器傳回一個 session ID，之後伺服器可以用此ID查訊使用者資訊&lt;&#x2F;p&gt;
&lt;h3 id=&quot;chang-jian-wen-ti&quot;&gt;常見問題&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;需要資料庫統一儲存登錄資訊&lt;&#x2F;li&gt;
&lt;li&gt;如何登出使用者？&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;json-web-token-de-qian-zai-wen-ti&quot;&gt;JSON Web Token 的潛在問題&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;XSS, CSRF, Replay attack, MITM&lt;&#x2F;li&gt;
&lt;li&gt;JWT 透過數位簽章跟加密來防止竄改或是竊取裡面的資料，但是 JWT 被儲存在
local storage 或是 cookies 裡面的時候，同一個 domain 的 JavaScript 同
樣都可以存取這些 JWT，因此有潛在的 XSS 或是 CSRF 攻擊的風險。&lt;&#x2F;li&gt;
&lt;li&gt;JWT 的大小比單純的 session ID 大的多&lt;&#x2F;li&gt;
&lt;li&gt;保護簽章用的密文或是私鑰變得非常重要&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;ru-he-ying-dui-zhong-bo-gong-ji&quot;&gt;如何應對重播攻擊&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;使用較短的有效期限&lt;&#x2F;li&gt;
&lt;li&gt;客戶端定期更新Token&lt;&#x2F;li&gt;
&lt;li&gt;伺服器端使用黑名單阻擋有問題的客戶&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;ba-jwt-huo-shi-session-id-cun-zai-cookie-de-hao-chu&quot;&gt;把 JWT 或是 session ID 存在 cookie 的好處&lt;&#x2F;h2&gt;
&lt;p&gt;Browser 會自動把 cookie 包含在 header 裡面所以適合用來保護網頁的存取&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;結論&lt;&#x2F;h2&gt;
&lt;p&gt;Don’t use JWT for persistent, long-lived data.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;can-kao-wen-xian&quot;&gt;參考文獻&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ponyfoo.com&#x2F;articles&#x2F;json-web-tokens-vs-session-cookies&quot;&gt;https:&#x2F;&#x2F;ponyfoo.com&#x2F;articles&#x2F;json-web-tokens-vs-session-cookies&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;by.jtl.xyz&#x2F;2016&#x2F;06&#x2F;the-unspoken-vulnerability-of-jwts.html&quot;&gt;http:&#x2F;&#x2F;by.jtl.xyz&#x2F;2016&#x2F;06&#x2F;the-unspoken-vulnerability-of-jwts.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;37582444&#x2F;jwt-vs-cookies-for-token-based-authentication&quot;&gt;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;37582444&#x2F;jwt-vs-cookies-for-token-based-authentication&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dzone.com&#x2F;articles&#x2F;jwtjson-web-tokens-are-better-than-session-cookies&quot;&gt;https:&#x2F;&#x2F;dzone.com&#x2F;articles&#x2F;jwtjson-web-tokens-are-better-than-session-cookies&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;cryto.net&#x2F;~joepie91&#x2F;blog&#x2F;2016&#x2F;06&#x2F;13&#x2F;stop-using-jwt-for-sessions&#x2F;&quot;&gt;http:&#x2F;&#x2F;cryto.net&#x2F;~joepie91&#x2F;blog&#x2F;2016&#x2F;06&#x2F;13&#x2F;stop-using-jwt-for-sessions&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jwt.io&#x2F;&quot;&gt;https:&#x2F;&#x2F;jwt.io&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>簡單把 CSS 變成非同步讀取</title>
        <published>2019-07-28T00:00:00+00:00</published>
        <updated>2019-07-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/simpler-way-to-load-css-async/"/>
        <id>https://kanru.info/simpler-way-to-load-css-async/</id>
        
        <content type="html" xml:base="https://kanru.info/simpler-way-to-load-css-async/">&lt;p&gt;在 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.smashingmagazine.com&#x2F;the-smashing-newsletter&#x2F;smashing-newsletter-issue-234&#x2F;#a1&quot;&gt;Smashing Newsletter&lt;&#x2F;a&gt; 看到的，只要 &lt;code&gt;&amp;lt;link&amp;gt;&lt;&#x2F;code&gt; 標籤就可以簡單把 CSS 變成非同步讀取：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;path&#x2F;to&#x2F;my.css&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        media&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;print&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; onload&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;media&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;all&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;有用過 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developers.google.com&#x2F;speed&#x2F;pagespeed&#x2F;insights&#x2F;&quot;&gt;Google PageSpeed Insight&lt;&#x2F;a&gt; 分析網站的，應該都有看過如下的結果：&lt;&#x2F;p&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;2019-07-28_17-23-21_screenshot.webp&quot; type=&quot;image&#x2F;webp&quot;&gt;
  &lt;source srcset=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;2019-07-28_17-23-21_screenshot.png&quot; type=&quot;image&#x2F;png&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;2019-07-28_17-23-21_screenshot.png&quot; width=&quot;100%&quot; alt=&quot;PageSpeed screenshot&quot;&gt;
&lt;&#x2F;picture&gt;
&lt;p&gt;原因是因為瀏覽器在處理 CSS 與 JavaScript 檔案的時候會阻擋其他資源的載入。解決的
方法裡，JavaScript 類型的資源可以標示 &lt;code&gt;defer&lt;&#x2F;code&gt; 或是 &lt;code&gt;async&lt;&#x2F;code&gt; 屬性讓瀏覽器知道我們
希望可以非同步的載入執行此 JavaScript 檔案，可惜 CSS 並沒有類似的屬性可以使用。&lt;&#x2F;p&gt;
&lt;p&gt;一般處理 CSS 的載入有兩個策略：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;盡量把最重要的樣式內嵌(inline)在 HTML 內，&lt;&#x2F;li&gt;
&lt;li&gt;用 JavaScript 動態的插入 &lt;code&gt;&amp;lt;link&amp;gt;&lt;&#x2F;code&gt; 標籤。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;策略 1. 應該不難理解，因為都已經要下載 HTML 了，不如把少部份最重要的樣式直接也寫
在裡面，可以省下一次網路請求。策略 2. 則是把其他剩餘的樣式集中在一起，用
JavaScript 在網頁讀取完畢後再動態的插入 &lt;code&gt;&amp;lt;link&amp;gt;&lt;&#x2F;code&gt; 標籤讓瀏覽器套用。&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;filamentgroup&#x2F;loadCSS&quot;&gt;LoadCSS&lt;&#x2F;a&gt;
似乎是個常用的 library 但是我沒有用過，因為我不喜歡這種需要很多 JavaScript 的解
法。&lt;&#x2F;p&gt;
&lt;p&gt;這次看到的這個方法非常簡單，原本的 CSS 標籤如下：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;path&#x2F;to&#x2F;my.css&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;只要再加上 &lt;code&gt;media=&quot;print&quot;&lt;&#x2F;code&gt; 以及 &lt;code&gt;onload=&quot;this.media=&#x27;all&#x27;&quot;&lt;&#x2F;code&gt; 就可以讓瀏覽器非同步載入 CSS 了！&lt;&#x2F;p&gt;
&lt;p&gt;原理是，瀏覽器在遇到非使用中的 &lt;code&gt;media&lt;&#x2F;code&gt; 時，只會下載但是不會套用樣式，所以設定
&lt;code&gt;media&lt;&#x2F;code&gt; 為 &lt;code&gt;print&lt;&#x2F;code&gt; 可以避免套用 CSS 的時候延遲其他的資源的載入，而 &lt;code&gt;onload&lt;&#x2F;code&gt; 的部
份則是在等到其他資源都載入完畢後，才把 &lt;code&gt;media&lt;&#x2F;code&gt; 切回 &lt;code&gt;all&lt;&#x2F;code&gt; 或是 &lt;code&gt;screen&lt;&#x2F;code&gt; 讓瀏覽器
套用樣式。雖然還是需要一點點 CSS，整體看起來清爽許多，也不需要額外的 library！&lt;&#x2F;p&gt;
&lt;p&gt;如果需要支援沒有開啟 JavaScript 的瀏覽器，記得加上 fallback：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;noscript&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;path&#x2F;to&#x2F;my.css&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;noscript&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;完整的範例如下：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;path&#x2F;to&#x2F;my.css&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        media&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;print&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; onload&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;media&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;all&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;noscript&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;path&#x2F;to&#x2F;my.css&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;noscript&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我迫不及待的立刻試用了😊 ∎&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>CSS Text 3: Segment Break Transformation Rules</title>
        <published>2017-07-08T18:36:54-07:00</published>
        <updated>2017-07-08T18:36:54-07:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/firefox-css-text-3-segment-break-transformation-rules-en/"/>
        <id>https://kanru.info/firefox-css-text-3-segment-break-transformation-rules-en/</id>
        
        <content type="html" xml:base="https://kanru.info/firefox-css-text-3-segment-break-transformation-rules-en/">&lt;p&gt;At the end of last year, I used my free time&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#have-time&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; to fix Firefox
Implementation of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;drafts.csswg.org&#x2F;css-text&#x2F;#line-break-transform&quot;&gt;Segment Break Transformation Rules&lt;&#x2F;a&gt;
(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1081858&quot;&gt;Bug 1081858&lt;&#x2F;a&gt;). Starting from Firefox 52, the line breaks between Chinese and Chinese in HTML
will not be displayed as blank in the browser.&lt;&#x2F;p&gt;
&lt;div style=&quot;display: flex; flex-wrap: wrap; align-items: baseline&quot;&gt;
&lt;figure style=&quot;flex: 1&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;css-text-3-segment-break-transformation-rules-source.png&quot; style=&quot;width: 72px&quot;&gt;
&lt;figcaption&gt;Source&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure style=&quot;flex: 1&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;css-text-3-segment-break-transformation-rules-before.png&quot; style=&quot;width: 199px&quot;&gt;
&lt;figcaption&gt;Chromium 59&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure style=&quot;flex: 1&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;css-text-3-segment-break-transformation-rules-after.png&quot; style=&quot;width: 183px&quot;&gt;
&lt;figcaption&gt;Firefox 56&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;When using a plain text editor to compose a document, for the comfort of
reading, we often set the line width to wrap at 80 characters. This width can
accommodate forty Chinese characters, which is also comfortable for reading
Chinese. With the advancement of software and hardware, the display content is
no longer limited to a fixed width. Many typesetting software can reformat the
content and display it in a different environment.&lt;&#x2F;p&gt;
&lt;p&gt;In the process of formatting from the original document to the content displayed
on the screen, these software will reorganize the text of the same paragraph
into a text stream, remove or insert new line breaks, and retype it in the new
container width; the problem is that many latin scripts use white space as the
separation between words, and the algorithm of typesetting software also assumes
this. As a result, the line breaks removed in the original document are replaced
with white space by default.&lt;&#x2F;p&gt;
&lt;p&gt;Nowadays, many content on the Internet is written in Markdown format, and most
of the Markdown translation programs retain the line breaks of the original
text&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#markdown&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, so many Chinese content on the Internet will find such
problems. If you don’t read it carefully, you may not notice it, but once you
notice it, it’s hard to ignore this typographic flaw, especially if the web page
uses justified layout.&lt;&#x2F;p&gt;
&lt;p&gt;This problem appears not only on web pages, but also in many applications that
automatically reformat the text stream. For example, the Chinese package XeCJK
of XeLaTeX also deals with similar problems. In the past, I used to modify the
back-end of the program to solve this problem, but I have been looking for a way
to fix it once and for all, directly in the browser.&lt;&#x2F;p&gt;
&lt;p&gt;Later I found that CSS Text 3 has specifications in this regard.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;4.1.2. Segment Break Transformation Rules&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When white-space is pre, pre-wrap, or pre-line, segment breaks are
not collapsible and are instead transformed into a preserved line
feed (U+000A).&lt;&#x2F;p&gt;
&lt;p&gt;For other values of white-space, segment breaks are collapsible. As
with spaces, any collapsible segment break immediately following
another collapsible segment break is removed. Then the remaining
segment breaks are either transformed into a space (U+0020) or
removed depending on the context before and after the break:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If the character immediately before or immediately after the
segment break is the zero-width space character (U+200B), then the
break is removed, leaving behind the zero-width space.&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise, if the East Asian Width property [UAX11] of both the
character before and after the segment break is F, W, or H (not
A), and neither side is Hangul, then the segment break is removed.&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise, the segment break is converted to a space (U+0020).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;After actually starting to implement it, I found that Firefox had done some
processing for Chinese early on, but this code incorrectly judged the boundaries
of Chinese characters, making this code doing nothing after it was written. The
original writing of this program is rather difficult to understand (using
negative array index), and the lack of relevant tests has led to no one to find
this error&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#coverage&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, and even after I fixed this code, some
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1369985&quot;&gt;bugs&lt;&#x2F;a&gt; in the original layout engine were exposed.&lt;&#x2F;p&gt;
&lt;p&gt;My colleague Jeremy later further &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1316482&quot;&gt;modified&lt;&#x2F;a&gt; Gecko’s algorithm to
make it conform to the current spec behavior.&lt;&#x2F;p&gt;
&lt;p&gt;For other browsers, IE already has partial support. Now if the same spec is
implemented in Blink&#x2F;Webkit, most web pages will have no problems. Servo
currently only has a simple algorithm in this part, which is also an opportunity
to contribute!&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;have-time&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;This bug has been in my “If I have time” list before, and usually I don’t have time to do it. 😆&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;markdown&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;The original Markdown is just simple text substitution with perl&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;coverage&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;That’s why test coverage is very important!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-tw">
        <title>CSS 文字級別 3：段落分隔變換規則</title>
        <published>2017-07-08T18:36:54-07:00</published>
        <updated>2017-07-08T18:36:54-07:00</updated>
        
        <author>
          <name>
            
              Kan-Ru Chen
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kanru.info/firefox-css-text-3-segment-break-transformation-rules/"/>
        <id>https://kanru.info/firefox-css-text-3-segment-break-transformation-rules/</id>
        
        <content type="html" xml:base="https://kanru.info/firefox-css-text-3-segment-break-transformation-rules/">&lt;p&gt;去年年底，我利用閒暇時間&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#have-time&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;修好了 Firefox
的 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;drafts.csswg.org&#x2F;css-text&#x2F;#line-break-transform&quot;&gt;Segment Break Transformation Rules&lt;&#x2F;a&gt; 實做
(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1081858&quot;&gt;Bug 1081858&lt;&#x2F;a&gt;)。從 Firefox 52 開始，HTML 裡面中文與中文之
間的換行，瀏覽器排版時不會顯示為空白。&lt;&#x2F;p&gt;
&lt;div style=&quot;display: flex; flex-wrap: wrap; align-items: baseline&quot;&gt;
&lt;figure style=&quot;flex: 1&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;css-text-3-segment-break-transformation-rules-source.png&quot; style=&quot;width: 72px&quot;&gt;
&lt;figcaption&gt;Source&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure style=&quot;flex: 1&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;css-text-3-segment-break-transformation-rules-before.png&quot; style=&quot;width: 199px&quot;&gt;
&lt;figcaption&gt;Chromium 59&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;figure style=&quot;flex: 1&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;kanru.info&#x2F;blog&#x2F;assets&#x2F;content&#x2F;css-text-3-segment-break-transformation-rules-after.png&quot; style=&quot;width: 183px&quot;&gt;
&lt;figcaption&gt;Firefox 56&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;使用純文字編輯器撰寫文檔時，為了閱讀的舒適性，常常我們會在行寬
80 個字元的時候換行，這個寬度可以容納四十個中文字，剛好也是可以舒
適閱讀中文的寬度。隨著軟體硬體的進步，顯示內容不再侷限於固定的寬度，
很多排版軟體都可以把內文重新排版後顯示在不一樣的環境。&lt;&#x2F;p&gt;
&lt;p&gt;從原始文檔轉換為螢幕顯示內容的排版過程，這些軟體會把同一段落的文字重組
為文字流，移除換行或是插入新的換行，重新排版於新的容器寬度內；問題在於
很多拼音文字使用空白作為字與字之間的分隔，排版軟體的演算法也如此假設，
結果原本文檔內被移除的換行就預設被取代為空白。&lt;&#x2F;p&gt;
&lt;p&gt;現在很多網路上的內容都是用 Markdown 語法編寫的，大部分的 Markdown 轉譯
程式都會保留原本內文的換行&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#markdown&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;，所以很多網路上的中文內容都會發
現這樣的問題。不仔細看的話也許不會注意到，但是一旦注意到了就很難忽略這
個排版的瑕疵，如果網頁使用齊頭齊尾排版的話尤其明顯。&lt;&#x2F;p&gt;
&lt;p&gt;這個問題不只出現在網頁上，也出現在很多會自動重新排版文字流的應用程式中，
例如 XeLaTeX 的中文套件 XeCJK 也要處理類似的問題。過去我經常修改程式後
端來解決這個問題，但是也一直在尋找一勞永逸的，直接在瀏覽器裡面修正的方法。&lt;&#x2F;p&gt;
&lt;p&gt;後來找到 CSS Text 3 有這方面的規範。實際開始修改後我發現其實 Firefox
在很早就有針對中文做些處理，但是這段程式碼錯誤判斷中文字的邊界，使得這
段程式碼從寫完開始就沒有任何作用。這段程式原本的寫法就比較難懂(使用負
的陣列索引)，缺乏相關的測試導致一直沒有人發現這個錯誤&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#coverage&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;，結果
甚至在我修好這段程式碼之後還暴露了原本排版引擎的一
些 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1369985&quot;&gt;bug&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;同事 Jeremy 後來進一步&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1316482&quot;&gt;修改&lt;&#x2F;a&gt;了 Gecko 的演算法，符合目前
spec 規範的行為。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;4.1.2. Segment Break Transformation Rules&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When white-space is pre, pre-wrap, or pre-line, segment breaks are
not collapsible and are instead transformed into a preserved line
feed (U+000A).&lt;&#x2F;p&gt;
&lt;p&gt;For other values of white-space, segment breaks are collapsible. As
with spaces, any collapsible segment break immediately following
another collapsible segment break is removed. Then the remaining
segment breaks are either transformed into a space (U+0020) or
removed depending on the context before and after the break:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If the character immediately before or immediately after the
segment break is the zero-width space character (U+200B), then the
break is removed, leaving behind the zero-width space.&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise, if the East Asian Width property [UAX11] of both the
character before and after the segment break is F, W, or H (not
A), and neither side is Hangul, then the segment break is removed.&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise, the segment break is converted to a space (U+0020).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;其他瀏覽器的話，IE 原本就有部份支援，只要在 Blink&#x2F;Webkit 也實做同樣的
spec，這樣大部分的網頁就都不會有問題了。Servo 目前在這部份也是只有簡單
的演算法，也是貢獻的機會！&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;have-time&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;之前這個 Bug 一直在我的「如果我有時間」清單裡，通常就是不會有時間做啦 😆&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;markdown&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;原本的 Markdown 是用 perl 做簡單的文字取代&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;coverage&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;所以說 test coverage 很重要！&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
</feed>
