<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://chameth.com/">
    <title>Chameth.com - posts like exposing-game-servers-over-tailscale, utility-user-stories but not tailscale-docker-gotcha, why-you-should-be-using-https</title>
    <subtitle>Personal homepage of Chris Smith</subtitle>
    <link href="https://chameth.com/feeds/posts/like/exposing-game-servers-over-tailscale,utility-user-stories/unlike/tailscale-docker-gotcha,why-you-should-be-using-https/" rel="self"/>
    <link href="https://chameth.com/"/>
    <icon>https://chameth.com/favicon.png</icon>
    <updated>2025-12-07T00:00:00Z</updated>
    <id>https://chameth.com/</id>
    <author>
        <name>Chris Smith</name>
    </author>
    <entry>
        <title>Exposing game servers over Tailscale</title>
        <link href="https://chameth.com/exposing-game-servers-over-tailscale/"/>
        <updated>2025-12-07T00:00:00Z</updated>
        <id>https://chameth.com/exposing-game-servers-over-tailscale/</id>
        <content xml:lang="en" type="html">&lt;p&gt;I’ve recently been playing a lot of &lt;a href=&#34;https://factorio.com/&#34;&gt;Factorio&lt;/a&gt; with a friend. I’ve been
hosting, but my desktop computer is behind far too many layers of NAT, and I can’t be bothered
dealing with setting up port forwards. Up until today we made do with Steam’s networking support,
which in our case ended up relaying the connection via one of their servers. This is amazing as
a free, no-hassle service, but the performance was so-so. We’d get random lag spikes, or the
initial map download would crawl along at about 1/100th of the speed we should be able to get.
We’re both fans of &lt;a href=&#34;https://tailscale.com/&#34;&gt;Tailscale&lt;/a&gt; though, so maybe there’s an easy solution
there?&lt;/p&gt;
&lt;p&gt;Tailscale even have an article on &lt;a href=&#34;https://tailscale.com/blog/factorio-multiplayer-video&#34;&gt;sharing a Factorio server&lt;/a&gt;
but it’s a ten minute long video&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and focused on setting up an actual dedicated server in Docker
rather than just clicking the convenient “Host” button in the game client. What we did instead was:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I shared my desktop’s Tailscale node with my friend, by creating a sharing link in the &lt;a href=&#34;https://login.tailscale.com/admin/machines&#34;&gt;Admin console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;My friend accepted the link, and could then see my machine in their admin panel&lt;/li&gt;
&lt;li&gt;I used &lt;code&gt;tailscale lock&lt;/code&gt; to sign my friend’s desktop’s node key, as I have &lt;a href=&#34;https://tailscale.com/kb/1226/tailnet-lock&#34;&gt;tailnet lock&lt;/a&gt; enabled&lt;/li&gt;
&lt;li&gt;I added a grant to my tailnet’s ACL to permit access from my friend to the specific IP/port on my tailnet:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;friend@example.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;dst&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;100.0.0.42&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;fd7a::42&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;			&lt;span class=&#34;chroma-nt&#34;&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;chroma-p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;udp:34197&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;		&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After this, my friend could connect to Factorio by entering the Tailscale IP address, or the Tailscale hostname (as they use &lt;a href=&#34;https://tailscale.com/kb/1081/magicdns&#34;&gt;MagicDNS&lt;/a&gt;).
Running &lt;code&gt;tailscale status&lt;/code&gt; shows that Tailscale managed to establish a direct connection despite the many layers of NAT involved,
and the performance improvements were significant and immediate.&lt;/p&gt;
&lt;p&gt;We did a bit of testing, and confirmed that no other services on my machine are accessible. And sharing a machine by default
only allows it to accept incoming connections, so I can’t get unwanted access to anything on my friend’s Tailnet, either.&lt;/p&gt;
&lt;p&gt;I’ve &lt;a href=&#34;https://chameth.com/how-i-use-tailscale/&#34;&gt;written before&lt;/a&gt; about all the different ways I use Tailscale; this is yet
another new way it’s solved a problem for me.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I’m not a fan of video tutorials at the best of times, but especially not when I want to do something as quickly as possible so I can get back to &lt;del&gt;my addiction&lt;/del&gt; growing the factory. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>On the utility of user stories</title>
        <link href="https://chameth.com/utility-user-stories/"/>
        <updated>2021-10-16T00:00:00Z</updated>
        <id>https://chameth.com/utility-user-stories/</id>
        <content xml:lang="en" type="html">&lt;p&gt;User stories are a staple of most agile methodologies. You’d be hard-pressed to find
an experienced software developer that’s not come across them at some point in their
career. In case you haven’t, they look something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a frequent customer, &lt;br/&gt;
I want to be able to browse my previous orders, &lt;br/&gt;
So that I can quickly re-order products.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They provide a persona (in this case “a frequent customer”), a goal (“browse my previous
orders”) and a reason (“so that I can quickly re-order products”). This fictitious user
story would probably rank among one of the better ones I’ve seen. More typically you end
up with something like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a user, &lt;br/&gt;
I want to be able to login,&lt;br/&gt;
So that I can browse while logged in.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This doesn’t really provide a persona or any proper reasoning. It’s just a straight-forward
task pretending to be a user story. If this is written in an issue then it provides no
extra information over one that simply says “Allow users to login”. In fact, because it’s
expressed so awkwardly I’d argue that it’s worse.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;This kind of task-disguised-as-a-user-story problem becomes more obvious when people try
to write technical tasks in the same way:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a developer,&lt;br/&gt;
I want to refactor the JobFactory,&lt;br/&gt;
So that I can work with it more efficiently in the future.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This just says “Refactor the JobFactory”. If you wrote that in a ticket you’d probably
feel bad for not describing it very well, but somehow when it’s dressed up as a user
story it feels more valuable.&lt;/p&gt;
&lt;h3 id=&#34;does-the-user-really-want-that&#34;&gt;Does the user really want that?&lt;/h3&gt;
&lt;p&gt;One thing that really irks me about user stories is that it lets you twist your
business objectives into sounding like they’re the user’s idea: the story becomes
a post-hoc justification for a task you decided was required.&lt;/p&gt;
&lt;p&gt;Say you’re making a mobile app for an online book store, and your team for whatever
reason has a target of increasing the number of users who view the daily book-of-the-day
offer. Maybe you do some interviews and users tell you they forget to check in each
day to see what the offer is. How can you funnel more users there?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a mobile app user, &lt;br/&gt;
I want to receive a push notification whenever a new book-of-the-day is available, &lt;br/&gt;
So that I have the chance to buy the book.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some users probably do want such a thing, but I’d argue the vast majority of them
do not. Imagine if every app on your phone alerted you whenever there was a new
deal, or popped up a dialog whenever you went near a physical shop they had
vouchers for…&lt;/p&gt;
&lt;p&gt;Obviously in the cold reality of capitalism businesses make money by doing things
not strictly in the interests of users&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. When written up like this it becomes so
painfully disingenuous, though. Despite writing a user story that starts with
the words “As a user”, you’re not really putting yourself in the user’s shoes.&lt;/p&gt;
&lt;h3 id=&#34;who-even-is-the-user&#34;&gt;Who even is the user?&lt;/h3&gt;
&lt;p&gt;One of the big benefits of user stories comes from using &lt;em&gt;personas&lt;/em&gt;
to describe users. It’s also one of the things that’s rarely actually done, at
least in my experience. Looking at Google Trends you can see the rise of searches
for “user story” as agile slowly takes over the world, but the movement in
searches about personas is very slight:&lt;/p&gt;
&lt;figure class=&#34;image center&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/utility-user-stories/google-trends-story-persona.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/utility-user-stories/google-trends-story-persona.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/utility-user-stories/google-trends-story-persona.png&#34; alt=&#34;Graph showing relative search frequency for user stories against user personas&#34; loading=&#34;lazy&#34; width=&#34;1000&#34; height=&#34;550&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Graph showing relative search frequency for user stories against user personas&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A lot of the time stories are just written with “As a user”, or have some
adjectives tacked on to the start (“as a mobile user”, “as a logged-in user”).
The best I’ve ever seen in the wild was specifying the class of user like
in the example at the start: “as a frequent customer”, or “as a visually impaired user”.&lt;/p&gt;
&lt;p&gt;The problem with using these classes is it requires you to come up with them
when you’re writing the story. Maybe one day you think about visually impaired
users, but the next you don’t. Maybe Bob thinks about certain classes of users,
but Alice concentrates on different ones.&lt;/p&gt;
&lt;p&gt;The ideal way to solve this is to come up with personas that all the team
understand. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kiera is addicted to books. She reads across genres, and often buys books
to gift to her friends and family. She has piles and piles of books to
read, but that doesn’t stop her ordering more if she sees a good deal.
She likes receiving new books almost as much as reading them, and opts
for the fastest delivery available.&lt;/li&gt;
&lt;li&gt;Sharon is a slow, methodical reader. She buys one book at a time when she’s
close to finishing her current one, and gifts or resells her old books.
She almost exclusively reads Science Fiction, and will generally read
complete series from start to finish. She’s short-sighted and often
struggles when using mobile phones or computers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You’d probably want 2-4 personas that collectively represent most facets of your
userbase. They can be a lot more fleshed out than these - if you search for
example user personas you’ll find many beautifully presented examples that
have complete backgrounds including hobbies, education levels, and so on.
Even with this minimal level of detail, though, I’d argue they’re more useful
than just writing things in a standard user story form.&lt;/p&gt;
&lt;p&gt;Thinking back to the story about push notifications, writing it to use one
of these two personas forces you to think about the trade-offs involved. Kiera
probably &lt;em&gt;would&lt;/em&gt; like a notification, but it would annoy and possibly confuse
Sharon. This then leads you down the path of considering how to accommodate
both types of user — maybe adding it as an option, or doing some fancy machine
learning, etc. The conversation is now focused around the users, rather than
steamrolling over them to reach a business objective.&lt;/p&gt;
&lt;h3 id=&#34;youre-holding-it-wrong&#34;&gt;You’re holding it wrong&lt;/h3&gt;
&lt;p&gt;The problems I’ve described are not a problem with user stories &lt;em&gt;per se&lt;/em&gt;, but
rather common issues with how they’re used. But there’s only so many times you
can tell people they’re holding a tool wrong before you have to accept that maybe
the tool was badly designed.&lt;/p&gt;
&lt;p&gt;Considering features from the perspective of multiple personas is the single
best thing you can possibly do to ensure you’re providing value to your users.
You don’t even need to write things in the typical user story style to benefit
from this.&lt;/p&gt;
&lt;p&gt;Tacking “As a user,” to the start of all your JIRA tickets isn’t being agile,
and isn’t good for users, even if it lets you tick a box somewhere. In some
cases this lip-service to users is actively detrimental to them. We should
be valuing users and personas over stilted templates and check-box exercises
(&lt;a href=&#34;https://agilemanifesto.org/&#34;&gt;remind you of anything?&lt;/a&gt;).&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;As a user, I want to pay more money for the things that I buy,
so that the company’s CEO can afford to go to space. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
</feed>
