<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Justin Garrison</title>
    <description>The latest articles on Forem by Justin Garrison (@rothgar).</description>
    <link>https://forem.com/rothgar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F153330%2F46f93eaf-1d7a-4ea9-96ac-b70dc2a7713b.png</url>
      <title>Forem: Justin Garrison</title>
      <link>https://forem.com/rothgar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rothgar"/>
    <language>en</language>
    <item>
      <title>How I Track My Resume in Git</title>
      <dc:creator>Justin Garrison</dc:creator>
      <pubDate>Mon, 23 Oct 2023 06:49:04 +0000</pubDate>
      <link>https://forem.com/rothgar/how-i-track-my-resume-in-git-15nh</link>
      <guid>https://forem.com/rothgar/how-i-track-my-resume-in-git-15nh</guid>
      <description>&lt;p&gt;I use Git to track my resume revisions and job applications.&lt;br&gt;
I've been using this method—or variation of it—for over 10 years. You can see my &lt;a href="https://justingarrison.com/resume"&gt;system engineer resume&lt;/a&gt; which is from my role-syseng branch.&lt;/p&gt;

&lt;p&gt;I made a video that walks through a couple different examples that you can check out here.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/W5pwShuW1yw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How to track your resume
&lt;/h2&gt;

&lt;p&gt;Here's an example repo that can give you an idea for how to &lt;a href="https://github.com/rothgar/track-your-resume-in-git/"&gt;track your resume in git&lt;/a&gt;. This is filled with dummy data and commit dates so you can understand what the repo looks like over time. This usually isn't important because I'm only ever dealing with HEAD on each branch, but it's cool to look back on all the roles and resumes I've had.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please make sure you create a private repo for your resume if you are going to have personal information like address, phone number, etc.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The basic idea for putting your resume in Git is so you can easily make changes to the resume without relying on &lt;code&gt;resume_company-job-v3_final.pdf&lt;/code&gt; naming. You don't have to track everything I do, but I have found value in tracking roles, companies, and applications in addition to my resume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Branches
&lt;/h2&gt;

&lt;p&gt;My repo heavily uses branches to keep resume format separate based on roles and applications to companies. I keep track of different application-specific resumes and maintain a main format, which hosts my primary resume.&lt;/p&gt;

&lt;p&gt;I don't apply to &lt;em&gt;that&lt;/em&gt; many different companies. When I look in my repo there are dozens, not hundreds over 10 years, and the vast majority of them are only 1 or 2 applications.&lt;/p&gt;

&lt;p&gt;Whenever I apply to a new company, I create a branch for that company. For instance, I might have one for Pixar or Google.&lt;/p&gt;

&lt;p&gt;For each specific role type I apply for, such as software engineering or management, I create a branch named &lt;code&gt;role-&lt;/code&gt;. I have less than 10 role branches because even new job titles can usually use the content from one of the other roles with minimal modifications.&lt;/p&gt;

&lt;p&gt;Each time I apply to a company for a role, I can take the resume from the relevant role branch and copy it to the company branch. To see an example please watch the video at the top of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep files consistant
&lt;/h2&gt;

&lt;p&gt;I keep file names consistant between branches to make it easier to know what I'm looking for. In general I have the following files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Working copy of my resume (&lt;code&gt;Justin Garrison.md&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Artifact copy of the resume (&lt;code&gt;Justin Garrison.pdf&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;readme.txt&lt;/code&gt; for information about tools used&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;justfile&lt;/code&gt; for common shortcuts&lt;/li&gt;
&lt;li&gt;Copy of the job description (&lt;code&gt;jd.pdf&lt;/code&gt; or &lt;code&gt;jd.txt&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All the other context about what company or role I'm working on come from the current Git branch.&lt;/p&gt;

&lt;p&gt;Whenever I apply for a job I save a copy of the job description and commit it to the repo with a &lt;code&gt;Apply: ...&lt;/code&gt; git commit. This gives me a history of applications and job descriptions with an easy way to search for applications at specific companies based on the branch.&lt;/p&gt;

&lt;p&gt;It has been a pain to apply through job portals like LinkedIn that don't let me download the job description. Usually, I save a link to the role in &lt;code&gt;jd.txt&lt;/code&gt;, but it's not the best solution. Please let me know if you know a way to save them to a PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's all there is to it. I started doing this just because I learned how to use Git in 2013 and wanted to use it for something. 10 Years later it has been an invaluable source of remembering just how far I've come in my career and some of the weird places I've applied.&lt;/p&gt;

&lt;p&gt;If you use git for tracking your resume and have a workflow you like please let me know by opening an issue on the &lt;a href="https://github.com/rothgar/track-your-resume-in-git/"&gt;example git repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>job</category>
      <category>resume</category>
      <category>git</category>
    </item>
    <item>
      <title>Mastodon instance with 6 files</title>
      <dc:creator>Justin Garrison</dc:creator>
      <pubDate>Wed, 07 Dec 2022 20:14:55 +0000</pubDate>
      <link>https://forem.com/rothgar/mastodon-instance-with-6-files-1mm4</link>
      <guid>https://forem.com/rothgar/mastodon-instance-with-6-files-1mm4</guid>
      <description>&lt;p&gt;Mastodon is built on the &lt;a href="https://activitypub.rocks/" rel="noopener noreferrer"&gt;ActivityPub&lt;/a&gt; protocol which is based on &lt;a href="https://www.w3.org/TR/activitystreams-core/" rel="noopener noreferrer"&gt;Activity Streams&lt;/a&gt; which stores data in &lt;a href="https://json-ld.org/primer/latest/" rel="noopener noreferrer"&gt;JSON Linked Data (JSON-LD)&lt;/a&gt;.&lt;br&gt;
All that means is Mastodon uses a lot of JSON that references other JSON.&lt;br&gt;
A Mastodon instance can serve those JSON documents any way it wants so long as they are UTF-8 encoded.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why would you do this?
&lt;/h2&gt;

&lt;p&gt;There are more than 17,000 Mastodon instances.&lt;br&gt;
Why would you implement one with static files?&lt;/p&gt;

&lt;p&gt;The first reason is security.&lt;br&gt;
Ars Technica has a &lt;a href="https://arstechnica.com/information-technology/2022/11/how-secure-a-twitter-replacement-is-mastodon-let-us-count-the-ways/" rel="noopener noreferrer"&gt;great article&lt;/a&gt; about some of the concerns with running large scale, multi-user social network servers.&lt;br&gt;
I have a lot of my own concerns about Mastodon that I'll save for a future post.&lt;/p&gt;

&lt;p&gt;There are scalability challenges on multiple levels.&lt;br&gt;
The size of databases and uploads is what most admins are concerned with, but number of active users and scale of a single user (e.g. celebrity, company, government) is what will really take down a social network.&lt;br&gt;
An instance with 30,000 active users &lt;a href="https://hub.fosstodon.org/more-upgrades-twitter-storm/" rel="noopener noreferrer"&gt;costs nearly $1900 per month.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If Mastodon is going to be adopted by the critical users it needs to grow, instances—many of which are run by volunteers—would be crushed under the operational and financial responsibility.&lt;br&gt;
Governments and companies aren't going to join shared servers; they're going to run their own instances on the domains they already own.&lt;br&gt;
The best way to scale and maintain a server is to not run one.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a server
&lt;/h2&gt;

&lt;p&gt;If you want to watch how I created these files check out the video.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/pyB7AFsQoJs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So let's create a Mastodon instance using JSON files.&lt;br&gt;
You can see the files on &lt;a href="https://github.com/rothgar/static-mastodon" rel="noopener noreferrer"&gt;GitHub.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The files are hosted at &lt;a href="https://mastodon.jgarr.net" rel="noopener noreferrer"&gt;https://mastodon.jgarr.net&lt;/a&gt; so you can test this for yourself by searching for the user @&lt;a href="mailto:justin@mastodon.jgarr.net"&gt;justin@mastodon.jgarr.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You only need 1 file but to make a more complete user we'll use these 6:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 files to create a user&lt;/li&gt;
&lt;li&gt;2 files to pretend we are popular&lt;/li&gt;
&lt;li&gt;2 pictures to make it look pretty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only required file is the user, but I wanted to show how easy it is to lie in the fediverse.&lt;/p&gt;

&lt;p&gt;Here are the files we'll be using.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── .well-known
│  └── webfinger    &amp;lt;- user discovery &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
├── banner.png      &amp;lt;- banner image &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
├── followers       &amp;lt;- how many followers &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
├── following       &amp;lt;- how many following &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
├── image.jpg       &amp;lt;- profile image &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
└── justin          &amp;lt;- user information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's explain what they do.&lt;/p&gt;

&lt;h3&gt;
  
  
  User discovery
&lt;/h3&gt;

&lt;p&gt;When you're using Mastodon you can search for a user on any Mastodon instance with &lt;code&gt;@user@domain&lt;/code&gt;.&lt;br&gt;
This is a short hand format which relies on &lt;a href="https://webfinger.net/" rel="noopener noreferrer"&gt;webfinger&lt;/a&gt; to translate a user at a domain into a standard URL.&lt;/p&gt;

&lt;p&gt;When you do this search your Mastodon server will query the external server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://server/.well-known/webfinger?resource=acct:user@domain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can bypass webfinger if you know how to fetch the user's information directly.&lt;br&gt;
If you search in Mastodon for &lt;code&gt;https://mastodon.jgarr.net/justin&lt;/code&gt; you'll get the same user.&lt;/p&gt;

&lt;p&gt;Here's the full access log so you can see the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"remote_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"remote_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"43636"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"proto"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HTTP/1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mastodon.jgarr.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/.well-known/webfinger?resource=acct:justin@mastodon.jgarr.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Wed, 30 Nov 2022 06:00:09 GMT"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"X-Forwarded-For"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"fd7a:115c:a1e0:ab12:4843:cd96:626f:140a"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"http.rb/5.1.0 (Mastodon/4.0.2; +https://mastodon.social/)"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"application/jrd+json, application/json"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Encoding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"gzip"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.000258163&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;203&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resp_headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Ranges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"bytes"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Content-Length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"203"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Caddy"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Etag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rm5bpw5n&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Last-Modified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Wed, 30 Nov 2022 05:39:32 GMT"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GET request technically uses the parameter &lt;code&gt;resource=acct:justin@mastodon.jgarr.net&lt;/code&gt; but with this static file example we only have one user on the domain so we'll ignore that part.&lt;br&gt;
If you want to have multiple users on the same domain you will have to handle parameters on the server side.&lt;br&gt;
Meaning, you can't do that with static files.&lt;/p&gt;

&lt;p&gt;This request returns the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acct:justin@mastodon.jgarr.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"self"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/activity+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/justin"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says where to go fetch the next JSON document at the &lt;code&gt;/justin&lt;/code&gt; path.&lt;br&gt;
Your Mastodon server will then go fetch that object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://server/justin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the full access log so you can see the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"remote_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"remote_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"43650"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"proto"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HTTP/1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mastodon.jgarr.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/justin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"application/activity+json, application/ld+json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Encoding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"gzip"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Wed, 30 Nov 2022 06:00:10 GMT"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"keyId=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://mastodon.social/actor#main-key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,algorithm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rsa-sha256&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,headers=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;(request-target) host date accept&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,signature=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;FIFlf1AqeWuDGqF0lNJy+eRoxsy83dZ44nyhe3O+kEAB4WE8rDNKwhrGrO
                67 GZQin3lbkMZ4BKpj71wAjhNbFW8p7FtdbvGGKPwceRh5gx1hh2iqdd / INw9NZFpRbPG4wq9oHNU4MIMikICcgNDeLzcYYXbUMaDDe9W4eVzExK6SF5ulJDY0tbZchT + kaZKZqGhae25FFLc0gEPEjA3XOiZRhsVU + 7 bGPyX8Lo2g6ebGuIHPynB5WYeOu8u8noEHtbxzx + LIQZJqy1gHDb9zKq09q + f2h6ngaegayxFxZOVLMVEbhpauq1iELxlPCXaWAcwFFmWS7tJZHpqnFBAXKg == &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"X-Forwarded-For"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fd7a:115c:a1e0:ab12:4843:cd96:626f:140a"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http.rb/5.1.0 (Mastodon/4.0.2; +https://mastodon.social/)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.000505261&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;912&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resp_headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Content-Length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"912"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Caddy"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Etag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rm5bxppc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Last-Modified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Wed, 30 Nov 2022 05:44:13 GMT"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Ranges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"bytes"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look at the access log you'll notice the signature in the header.&lt;br&gt;
This uses a keyId &lt;code&gt;https://mastodon.social/actor#main-key&lt;/code&gt; which is the instance that searched for the user.&lt;br&gt;
There's a signature which can be used to verify the correct server―or user―is making requests.&lt;/p&gt;

&lt;p&gt;If you want to you can skip webfinger by searching for a user by their URL directly.&lt;br&gt;
If you search in Mastodon for &lt;code&gt;https://mastodon.jgarr.net/justin&lt;/code&gt; you'll get the same user.&lt;/p&gt;

&lt;p&gt;That means we need 1 less file but it doesn't seem as magical as &lt;code&gt;@justin@mastodon.jgarr.net&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This returns our actual user document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://www.w3.org/ns/activitystreams"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://w3id.org/security/v1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/justin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"following"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/following"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"followers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/followers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inbox"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/inbox"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preferredUsername"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"justin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Justin Garrison"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Static mastodon server example."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://justingarrison.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"manuallyApprovesFollowers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"discoverable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2000-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nl"&gt;"icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/icon.jpg"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Image"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/image.png"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see your user's JSON document you can append &lt;code&gt;.json&lt;/code&gt; to your user's URL (e.g. &lt;a href="https://mastodon.social/@jgarr.json" rel="noopener noreferrer"&gt;https://mastodon.social/@jgarr.json&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Not everything in this example user document is required, but here's the first place we can lie about our account and make it look more legitimate.&lt;/p&gt;

&lt;p&gt;You'll noticed the published date &lt;code&gt;2000-01-01T00:00:00Z&lt;/code&gt; which means we created an account long before Mastodon existed.&lt;br&gt;
Not a big deal, but it's completely unverified.&lt;/p&gt;

&lt;p&gt;We also add an icon and image to the profile so it doesn't have the default image.&lt;br&gt;
The images are completely optional, but it adds legitemacy to a federated account posing as a real user.&lt;/p&gt;

&lt;p&gt;Because we own the domain and can lie about the accounts we can use a commonly misspelled domain or unicode to create fake accounts.&lt;br&gt;
We could easily use any domain to make accounts like &lt;code&gt;@charles@gov.co.uk&lt;/code&gt; or &lt;code&gt;@tim@apple.ceo&lt;/code&gt; (both of these domains are currently available).&lt;/p&gt;

&lt;p&gt;Mastodon puts the zero in zero trust.&lt;br&gt;
In reality, any completely decentralized system—like the internet—only has trust through reputation, but in Mastodon you can fake a repulation.&lt;/p&gt;

&lt;p&gt;Here's what the profile looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="/img/mastodon-user.png" class="article-body-image-wrapper"&gt;&lt;img src="/img/mastodon-user.png" alt="a screenshot of my fake user"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the user is requested your Mastodon instance will automatically fetch &lt;code&gt;/followers&lt;/code&gt; and &lt;code&gt;/following&lt;/code&gt;.&lt;br&gt;
Just like other documents these are reference documents to the actual data documents, but the data isn't verified so we can lie again.&lt;/p&gt;

&lt;p&gt;You'll notice this account has 1 million followers and follows 1 account.&lt;br&gt;
Both of which are not possible because even if you click the follow button the instance cannot acknowledge your request and this account has no keys so it cannot follow any accounts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://mastodon.jgarr.net/followers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the full access log so you can see the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"remote_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"remote_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"43692"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"proto"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HTTP/1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mastodon.jgarr.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/followers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http.rb/5.1.0 (Mastodon/4.0.2; +http
                s: //mastodon.social/)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"application/activity+json, application/ld+json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Encoding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"gzip"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Wed, 30 Nov 2022 06:00:12 GMT"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"keyId=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://mastodon.social/actor#main-key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,algorithm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rsa-sha256&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,headers=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;(request-target) host date
                accept&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;signature=\&lt;/span&gt;&lt;span class="s2"&gt;"wUAArkeEJh4yXkstcC8IgrnSlsRcledOUjo63nqRZrXI0RtoKo369/+j5K7bEFDoJ8psuCnnY9cW+KDgog7Gg2mQjAb1cZa2ffeqFY3PPXqpO+5entfRkAEyYBsrd3CiVn5wz0LEwbOs3XHe1w2wVgoIbSunCE/DN0Ra5tQLriITzBA5YzI26QuQSJzb5sMmMjiTiVocF/i0djqXfLmnjvhyaxsS0i0O8LfPHVPzSSGFHaqzawIL28MZu8J42ha//baJmP
                ozQQquFHKs7lcDcSSGtrvMGjfJYoFy4cMSsSqLH / 8 VRzNR0nXs47ydDwQ9XRpT55LPWL7uRQoeYBAkwA == &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"X-Forwarded-For"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fd7a:115c:a1e0:ab12:4843:cd96:626f:140a"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.000082826&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resp_headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Caddy"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Etag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rm1lyn6j&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Last-Modified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Mon, 28 Nov 2022 05:30:23 GMT"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Ranges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"bytes"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Content-Length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"235"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://mastodon.jgarr.net/following
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the full access log so you can see the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"remote_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"remote_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"43678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"proto"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HTTP/1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mastodon.jgarr.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/following"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Wed, 30 Nov 2022 06:00:11 GMT"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"Signatur
            e "&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"
            keyId = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://mastodon.social/actor#main-key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,algorithm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rsa-sha256&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,headers=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;(request-target) host date accept&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,signature=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a3EqAl1mUNhvTQvgWCng44mJpxSNcYo1CnTlUH8qy9i84S3bBSR6tIdHvK1dpoLI2+evgUvyLW0H8l20dG9jzLUIUPoXyTG+TapAKr6Z9i80F20IxoInQzoZVl3ytgkMqGw2EFV0fU2/K18/Z+
            wECJCQoFivt / QcMXPs7ox / EqxikZ + WyKsBX / TprzqFTSfg / ozpEluAxLmfNsN3IxYnb8XAGZlZC2n4Vkg9Ue + LYHH7PhLq3XAdQPgCKSI1IUxZVpeo0WttESxhAmoxVMd5bXSGJTVFInqKSH3J8UyhwbPKcCWm4oFnuVGAeZuL1UwyIQsiFgj53pU6oV + zwzZrJQ == &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"X-Forwarded-For"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fd7a:115c:a1e0:ab12:4843:cd96:626f:140a"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"
            http.rb / 5.1 .0(Mastodon / 4.0 .2; + https: //mastodon.social/)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"application/activity+json, application/ld+json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Encoding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"gzip"&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.000069315&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;230&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"resp_headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"Last-Modified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Mon, 28 Nov 2022 05:30:23 GMT "&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"
                Accept - Ranges "&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;" bytes "&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"
                Content - Length "&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;" 230 "&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"
                Server "&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;" Caddy "&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"
                Etag "&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rm1lyn6e&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;/following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.w3.org/ns/activitystreams"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/following"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OrderedCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"totalItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/following_accts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;/followers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.w3.org/ns/activitystreams"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/followers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OrderedCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"totalItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.jgarr.net/follower_accts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mastodon never validates the data in &lt;code&gt;/follower_accts&lt;/code&gt; that we claim holds our 1 million followers so we don't have to create that file.&lt;/p&gt;

&lt;p&gt;If you click the follow button your Mastodon instance will send a POST request to &lt;code&gt;/inbox&lt;/code&gt; with your user's key signature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST https://mastodon.jgar.net/inbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"remote_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"remote_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"42294"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"proto"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HTTP/1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mastodon.jgarr.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/inbox"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"application/activity+json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SHA-256=8we9H5V74oUdQr8R5vay/dyQEi0I2up5wwI7+9e8T70="&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"keyId=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;https://mastodon.social/users/jgarr#main-key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,algorithm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rsa-sha256&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,headers=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;(request-target) host date digest content-type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,signature=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hx1jRjCGyBfnI/Cak8ujAlfau5G1Ph+9niCFyRdm5J7b9wQGxbk+SbUhG0kV2L7W0h54JBc6htQhR8V+fFqxX+UiLXe1l7jRoBZOYSKq7UKqtogJwLLvS89DeDgWLWDPqbZ6W1FzU9MLUqJLTqFNnhgtOH+m+YhKEfjE35+65d5vmPUNjR8TRDAjXjMugMi3NmeaeA789NV3gs7GaGyfI734kGwvPDHcLp9MyDHqBivdDqmPAzXRP4gyrjqXHQRpxCdX7iinA/aqgnsNf2CoY/uH7M+z+zPWohlTvVDU2L+xeT2N7pXFc6WxREPV4ojZ+VxMzmIuzHkxW8TVpVNwMw==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"X-Forwarded-For"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fd7a:115c:a1e0:ab12:4843:cd96:626f:140a"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http.rb/5.1.0 (Mastodon/4.0.2; +https://mastodon.social/)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Content-Length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"779"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Accept-Encoding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"gzip"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Wed, 30 Nov 2022 07:01:44 GMT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.000112712&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"resp_headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Caddy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike the requests before, this request will use the mastodon.social user's signature and key instead of the instance actor account.&lt;br&gt;
My instance &lt;em&gt;should&lt;/em&gt; connect back to the mastodon.social server to verify the user's signature, but you'll notice the status 404 because I didn't implement following or create an inbox file.&lt;/p&gt;

&lt;p&gt;Even though the status is 404 the requesting server still shows a follow request is sent.&lt;br&gt;
If you cancel the request it will decrement the followers count.&lt;/p&gt;

&lt;h2&gt;
  
  
  What doesn't work
&lt;/h2&gt;

&lt;p&gt;Those 6 files is all you need to create a Mastodon user.&lt;br&gt;
Here are some caveats you may have already noticed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Following doesn't work&lt;/li&gt;
&lt;li&gt;Posts don't work&lt;/li&gt;
&lt;li&gt;Only 1 user per domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; create JSON objects with posts, replies, or anything you'd like, but Mastodon instances don't fetch posts from external users unless someone from that instance follows the user or has reposted one of their posts.&lt;br&gt;
I implemented a single post in the &lt;code&gt;/outbox&lt;/code&gt; file so if you want to see how they are structured you can browse the &lt;a href="https://github.com/rothgar/static-mastodon/blob/main/docs/outbox" rel="noopener noreferrer"&gt;source files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The instance is supposed to fetch pinned posts, but I couldn't figure out how that is implemented.&lt;br&gt;
If someone knows please reach out and let me know at my real mastodon account &lt;a href="https://mastodon.social/@jgarr" rel="noopener noreferrer"&gt;@jgarr@mastodon.social&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, we'll give this instance some of the functionality that doesn't work.&lt;br&gt;
We'll allow users to follow the account, and then let it create posts.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>github</category>
    </item>
    <item>
      <title>Reverse engineering a chrome extension</title>
      <dc:creator>Justin Garrison</dc:creator>
      <pubDate>Thu, 27 Jan 2022 06:22:35 +0000</pubDate>
      <link>https://forem.com/rothgar/reverse-engineering-a-chrome-extension-1e6h</link>
      <guid>https://forem.com/rothgar/reverse-engineering-a-chrome-extension-1e6h</guid>
      <description>&lt;p&gt;I've been using &lt;a href="https://www.getrevue.co"&gt;Revue&lt;/a&gt; for my &lt;a href="https://123dev.email"&gt;123dev&lt;/a&gt; newsletter and wanted an easier way to save URLs to include in future emails.&lt;br&gt;
If you're not familiar with it, Revue has a chrome extension so you can send URLs to a queue which shows up next to the editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YIVGUsCE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-sidebar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YIVGUsCE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-sidebar.png" alt="Revue sidebar showing staged links" width="841" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a really handy feature and I wanted to use it without the extension.&lt;br&gt;
Ideally, I could send these URL from my phone via a Siri Shortcut (I haven't figured this part out yet).&lt;/p&gt;

&lt;p&gt;The functionality wasn't exposed in their &lt;a href="https://www.getrevue.co/api"&gt;API docs&lt;/a&gt; so I'd have to figure out another way.&lt;br&gt;
I learned some new things exploring the extension so I thought I'd share how I did it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reverse engineering the extension
&lt;/h2&gt;

&lt;p&gt;The first thing I needed was to figure out what URLs the extension was calling.&lt;br&gt;
I tried watching Chrome dev tools for network calls, watching DNS requests, and tcpdump.&lt;/p&gt;

&lt;p&gt;Without having a man in the middle to decode https it wasn't going to work.&lt;br&gt;
Thankfully, someone pointed out the code is available if you have the extension installed.&lt;/p&gt;

&lt;p&gt;First, we need to get the extension ID from the &lt;a href="https://chrome.google.com/webstore/detail/revue-chrome/fdnhneinocoonabhfbmelgkcmilaokcg"&gt;installation URL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H79dZ3Wr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-extension.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H79dZ3Wr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-extension.png" alt="Revue extension in the app store" width="689" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The long string in the URL &lt;code&gt;fdnhneinocoonabhfbmelgkcmilaokcg&lt;/code&gt; will be in our home folder with the source code.&lt;/p&gt;

&lt;p&gt;On my computer it's under &lt;code&gt;$HOME/.config/google-chrome/Default/Extensions/fdnhneinocoonabhfbmelgkcmilaokcg&lt;/code&gt;.&lt;br&gt;
I opened the folder in vscode and looked at the &lt;code&gt;main.*.chunk.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;It was minified so first I had to unminify it as best as possible.&lt;br&gt;
Formatting the javascript was as good as I could get it.&lt;/p&gt;

&lt;p&gt;From there I looked for &lt;code&gt;POST&lt;/code&gt; url verbs to see what it was calling.&lt;br&gt;
I found this relevant code which looked like what I needed.&lt;br&gt;
It's calling &lt;code&gt;https://www.getrevue.co/extension/add&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uwkdYOt2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-code.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uwkdYOt2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-code.png" alt="A snippet of code from the Revue extension" width="880" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll see from the code the only thing it's sending is a &lt;code&gt;POST&lt;/code&gt; with a body.&lt;br&gt;
At this point I don't know what the body should be, but I'll try to figure that out later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting session cookie
&lt;/h2&gt;

&lt;p&gt;Now I need to jump over to Chrome to get my session cookie.&lt;br&gt;
Open getrevue.co in a tab and open dev tools.&lt;/p&gt;

&lt;p&gt;Go to the Application tab and then find Cookie in the left sidebar.&lt;br&gt;
Copy the value for _revue_session.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QQeFzUq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-cookie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QQeFzUq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-cookie.png" alt="a partial view of my user session cookie" width="671" height="359"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Send a curl request
&lt;/h2&gt;

&lt;p&gt;Now we need to send our request and see if it works.&lt;/p&gt;

&lt;p&gt;We still don't know what the body data should look like, but looking at the API objects that are documented I'm going to guess it needs a &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;url&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;COOKIE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your cookie session here"&lt;/span&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s2"&gt;"_revue_session=&lt;/span&gt;&lt;span class="nv"&gt;$COOKIE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"title": "TESTING", "url": "https://justingarrison.com"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    https://www.getrevue.co/extension/add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sure enough that worked!&lt;/p&gt;

&lt;p&gt;Here's a snippet of the response&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--myjQU6UN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-response.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--myjQU6UN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-response.png" alt="json output from the Revue API" width="712" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The response gives us a much better idea of the full body data we can use.&lt;br&gt;
Adding a description will be a minimal amount of information that would be useful.&lt;/p&gt;

&lt;p&gt;Now we can send items from the CLI but what about from iOS?&lt;/p&gt;

&lt;h2&gt;
  
  
  [WIP] Siri Shortcut
&lt;/h2&gt;

&lt;p&gt;Siri shortcuts are very powerful but also very cryptic.&lt;/p&gt;

&lt;p&gt;I was able to make a shortcut with the "Get contents of URL" function which is able to make a POST call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--93wUdCUj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-shortcut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--93wUdCUj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://justingarrison.com/img/revue-shortcut.png" alt="A screenshot from an ios siri shortcut" width="701" height="973"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can put in the URL, change the method to POST, and add a body with the required title and url variables.&lt;/p&gt;

&lt;p&gt;Unfortunately, when I try to use this shortcut from the share sheet I don't think it uses my session token so I never get authenticated to the API.&lt;/p&gt;

&lt;p&gt;If anyone knows a way to either open a Safari page and perform the action or a way to store an authentication token in the shortcut please reach out &lt;a href="https://twitter.com/rothgar"&gt;on twitter&lt;/a&gt; and let me know.&lt;/p&gt;

</description>
      <category>hacking</category>
      <category>newsletter</category>
      <category>chrome</category>
    </item>
    <item>
      <title>Debug Kubernetes Clusters in 8 Commands</title>
      <dc:creator>Justin Garrison</dc:creator>
      <pubDate>Wed, 20 Oct 2021 04:45:44 +0000</pubDate>
      <link>https://forem.com/rothgar/debug-kubernetes-clusters-in-8-commands-3i6</link>
      <guid>https://forem.com/rothgar/debug-kubernetes-clusters-in-8-commands-3i6</guid>
      <description>&lt;p&gt;I wrote this post for &lt;a href="https://thenewstack.io/living-with-kubernetes-debug-clusters-in-8-commands/"&gt;The New Stack&lt;/a&gt; and wanted to share it here on dev.to also.&lt;/p&gt;




&lt;p&gt;If you’ve lived with any system long enough, you have assuredly had to debug it. Kubernetes is no different. It is a distributed system with a lot of moving parts and sometimes those parts need to be understood by humans. In this post, we’ll look at 8 commands you can run to start debugging any Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;There are lots of optional components in a cluster, but we won’t focus on all of them here. We will discuss troubleshooting workloads in a future article. This post will focus on cluster operations. It will help you understand a cluster and make sure the core functionality — running pods — is available.&lt;/p&gt;

&lt;p&gt;This post will assume you have admin access to the cluster. We will pretend you’ve been handed a kubeconfig file with access to a cluster and you were told the cluster is broken. Where do you start?&lt;/p&gt;

&lt;p&gt;Here are the eight commands to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl version --short
kubectl cluster-info
kubectl get componentstatus
kubectl api-resources -o wide --sort-by name
kubectl get events -A
kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl run a --image alpine --command -- /bin/sleep 1d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down each command to understand why it’s important and what you should be looking for. For cluster debugging, we’re going to take a breadth first approach to understand what is in the cluster before we dive deep into workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl version --short
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--10Z1ybFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98b5slrzi63yccu29opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--10Z1ybFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98b5slrzi63yccu29opt.png" alt="command output showing kubernetes version"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this command, we’re looking to see what version of the API server is running. This gives us important information later when we’re troubleshooting specific errors and it’s very useful to know if we’re on an older cluster like 1.16.&lt;/p&gt;

&lt;p&gt;Knowing the version can also help us when searching for errors and reading changelogs. There may be known issues that require a version upgrade or a newly introduced bug. There are sometimes version compatibility issues between different components and knowing what version is running is the first step.&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl cluster-info
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gUn8P7Sp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4n7f2tbjlocof80b7gl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gUn8P7Sp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4n7f2tbjlocof80b7gl1.png" alt="command output showing kubernetes cluster endpoints"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we should understand where the cluster is running and if CoreDNS is running. You can parse the control plane URL to know if you’re dealing with a hosted cluster or something on-prem.&lt;/p&gt;

&lt;p&gt;In this example output, we can tell that we’re running an Amazon Elastic Kubernetes Service (Amazon EKS) cluster in the us-east-2 region. This information is also useful to look up if there is a current outage with your provider. You can look at your provider's service health dashboard to know if the current issues are with your cluster or something outside of it.&lt;/p&gt;

&lt;p&gt;This can also give you a clue if additional authentication will be needed for the cluster. There could be an AWS Identity and Access Management (IAM) permission issue or maybe you need to install an authentication plugin like the aws-iam-authenticator.&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl get componentstatus
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tCPBmq0L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2uai47zfl0zg3v5o67lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tCPBmq0L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2uai47zfl0zg3v5o67lc.png" alt="command output showing different kuberenetes components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command will be the easiest way to discover if your scheduler, controller-manager and etcd node(s) are healthy. These are all critical control plane components to run your pods. You should look for any component that doesn’t show an “ok” status and look for any errors.&lt;/p&gt;

&lt;p&gt;If you are using a cluster with a managed control plane (e.g. Amazon EKS) you may not have direct access to the scheduler or controller manager. Being able to see their status from this output is likely the easiest way to know whether something is wrong with etcd or another component.&lt;/p&gt;

&lt;p&gt;It’s also important to note that the componentstatus command is deprecated in the CLI but not yet removed. There isn’t currently a replacement for this command, but until it is removed from the CLI it is safe to use and very useful. Depending on your cluster it may require multiple commands to get similar output. There are design limitations to this command which is why it has been deprecated.&lt;/p&gt;

&lt;p&gt;An alternative option to see other health endpoints (including etcd) is &lt;code&gt;kubectl get --raw '/healthz?verbose'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Although this command does not show scheduler or controller-manager output, it adds a lot of additional checks that might be valuable if things are broken.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HVRCNrEe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ic199bczd0dwc3w4338g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HVRCNrEe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ic199bczd0dwc3w4338g.png" alt="command output showing lots of health endpoints"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl api-resources -o wide --sort-by name
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dwZpwEyz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92jtl2ta5s20et08syav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dwZpwEyz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92jtl2ta5s20et08syav.png" alt="command output showing all the different api resources in a cluster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the first command with a lot of information. We already know what version and where our cluster is running. At this point, we should know whether the control plane is healthy and now we need to look at some of the resources inside the cluster.&lt;/p&gt;

&lt;p&gt;I like to list all the resources sorted by name for consistency. It’s easier for me to scan the resources in alphabetical order. Adding -o wide will show the verbs available on each resource. This can be important because some resources do more than others. Knowing what verbs are available — or aren’t — will help narrow down where you should look for errors.&lt;/p&gt;

&lt;p&gt;Using this command will tell you what CRDs have been installed in your cluster and what API version each resource is at. This could give you some insights into looking at logs on controllers or workload definitions. Your workloads might be using an old alpha or beta API version but the cluster may only use v1 or apps/v1.&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl get events -A
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JBINozLT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lwmf4oq8wyoaw0u2vljk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JBINozLT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lwmf4oq8wyoaw0u2vljk.png" alt="command output showing all the events in a cluster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have an idea of what’s running in the cluster, we should look at what’s happening. If something broke recently, you can look at the cluster events to see what was happening before and after things broke. If you know there’s only a problem in a specific namespace, you can filter the events to just that namespace and block out some of the additional noise from healthy services.&lt;/p&gt;

&lt;p&gt;With this output, you should focus on the type of output, reason and object. With those three pieces of information you can narrow down what errors you’re looking for and which component may be misconfigured.&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl get nodes -o wide
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--suZ8AT__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1qugtk0cjvngo6c6z19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--suZ8AT__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1qugtk0cjvngo6c6z19.png" alt="command output showing the cluster nodes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nodes are a first class resource inside Kubernetes and are fundamental for pods to run. Using the -o wide option will tell us additional details like operating system (OS), IP address, and container runtime. The first thing you should look for is the status. If the node doesn’t say “Ready” you might have a problem — but not always.&lt;/p&gt;

&lt;p&gt;Look at the age of the nodes to see whether there’s any correlation between status and age. Maybe only new nodes have problems because something changed in the node image. The version will help you know quickly whether you have version skew on the kubelets and possibly have known bugs because of different versions between the kubelet and API server.&lt;/p&gt;

&lt;p&gt;The internal IP can be useful if you see IP addresses outside of the subnet. It’s possible a node started with an incorrect static IP address and your CNI cannot route traffic to a workload.&lt;/p&gt;

&lt;p&gt;OS image, kernel version and container runtime are all great indicators for possible differences causing a problem. You may only have a problem with a specific OS or runtime. This information will help you quickly zero in on a potential problem and know where to look deeper at logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl get pods -A -o wide
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sJBbqUEI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idfhmzcog5sd4bwnuzkx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sJBbqUEI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idfhmzcog5sd4bwnuzkx.png" alt="command output showing all pods in a cluster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, our last information-gathering command. As with listing nodes, you should first look at the status column and look for errors. The ready column will show how many pods are desired and how many are running.&lt;/p&gt;

&lt;p&gt;Using -A will list pods in all namespaces and -o wide will show us IP addresses, nodes and where the pods are nominated. Using the information from listing nodes, you can look at which pods are failing on which nodes. Correlating that information with details like OS, kernel, and container runtime might give you the insights you need to fix the cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  kubectl run a --image alpine --command -- /bin/sleep 1d
&lt;/h2&gt;

&lt;p&gt;Sometimes, the best way you can debug something is to start with the simplest example. This command doesn’t have any direct output, but you should see a running pod named “a” from it.&lt;/p&gt;

&lt;p&gt;If I’m not debugging something with other people, I like to name my debug containers single letters because it’s faster to type and it’s easy to iterate (e.g. b, c, d). I often like to keep old containers around while I’m debugging because sometimes it’s helpful to see what’s different with previous pods and it’s easier to leave them running — or crashing — than to search my terminal output history.&lt;/p&gt;

&lt;p&gt;If for some reason you don’t see a running pod from this command, then using &lt;code&gt;kubectl describe po a&lt;/code&gt; is your next best option. Look at the events to find errors for what may have gone wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZVk6PTXl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwjkqifh5tsh5dkn2qm4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZVk6PTXl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwjkqifh5tsh5dkn2qm4.png" alt="command output showing events for a pod"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With these commands, you should be able to get started with any cluster and know if it’s healthy enough to run a workload. There are additional things to consider such as CoreDNS scaling, load balancing, volumes, and central logging and metrics. The commands here should work in the cloud or on-prem.&lt;/p&gt;

&lt;p&gt;If you need to troubleshoot nodes or external resources (e.g. load balancers), then you should look at your controllers and API server logs for errors. Depending on what is broken you may need to look at kube-proxy, CNI plugins or service mesh sidecar container logs.&lt;/p&gt;

&lt;p&gt;Hopefully, these 8 commands will help you narrow down the scope of what could be broken in your cluster. If you don’t know what the problem is then starting with a breadth first search is the best approach. Pay attention to anomalies of mismatched versions and nodes or settings that are different. If you look for what has changed it can point you in the right direction to find and fix the problem quickly. We will look at how to debug workloads in your cluster in a future article.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>debug</category>
    </item>
    <item>
      <title>Kubernetes Cluster Upgrade Patterns</title>
      <dc:creator>Justin Garrison</dc:creator>
      <pubDate>Wed, 21 Apr 2021 17:01:08 +0000</pubDate>
      <link>https://forem.com/rothgar/kubernetes-cluster-upgrade-patterns-1b4c</link>
      <guid>https://forem.com/rothgar/kubernetes-cluster-upgrade-patterns-1b4c</guid>
      <description>&lt;p&gt;I wrote this article for &lt;a href="https://thenewstack.io/living-with-kubernetes-cluster-upgrades/" rel="noopener noreferrer"&gt;The New Stack&lt;/a&gt; and wanted to re-share it here for the dev.to community.&lt;/p&gt;




&lt;p&gt;If you’ve been using Kubernetes for any amount of time, you need to plan for regular upgrades. Starting with Kubernetes 1.19, each open source release provides one year of patches. You need to upgrade to the latest available minor or patch release to receive the security and bug fixes. But how can you upgrade a critical part of your infrastructure without downtime? This article will guide you through common patterns to consider when upgrading Kubernetes in any environment.&lt;/p&gt;

&lt;p&gt;We won’t dive into all of the tools and considerations to perform an upgrade. If you are using a cluster management tool or hosted Kubernetes service, you should consult your documentation for the best option for your environment. You also need to be aware that some workloads and environments may restrict what upgrade strategy you choose.&lt;/p&gt;

&lt;p&gt;We will discuss a few high-level patterns for cluster upgrades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In place&lt;/li&gt;
&lt;li&gt;Blue/Green&lt;/li&gt;
&lt;li&gt;Rolling&lt;/li&gt;
&lt;li&gt;Canary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These patterns are similar to application upgrade options, with some unique considerations because of their potential blast radius. Upgrading infrastructure can incur a considerable cost, depending on how long your upgrade takes and how large your environment is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control Plane Components
&lt;/h3&gt;

&lt;p&gt;The Kubernetes control plane consists of the Kubernetes API server, etcd database, controller manager, scheduler, and any additional controllers such as cloud or ingress that you may have in your environment. Upgrading the API server is the first step when upgrading a cluster. Kubernetes stores state in etcd and with any major application upgrades, you need to make sure you have at least one backup and that you’ve verified the backup can be restored. In some cases, an API server upgrade may also require an etcd upgrade, but we won’t be covering that in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Plane Components
&lt;/h3&gt;

&lt;p&gt;The Kubernetes data plane consists of the kubelet, a container runtime, and any network, logging, or storage drivers you use in your cluster workloads. For many clusters this will — at minimum — require a kube-proxy and CNI plugin update. Your data plane components must be equal to, or one minor version behind, your API server version. Ideally, your host OS, container runtime and data plane components can be upgraded independently from each other. Decoupling these components will ensure that you can upgrade quickly when there are bug fixes, new features, or security patches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes Hosted Service
&lt;/h2&gt;

&lt;p&gt;If you’re using a hosted Kubernetes service such as Amazon Elastic Kubernetes Service (EKS), control plane upgrades are handled for you. If you’re using a managed data plane service, such as Managed Node Groups (MNG), your data plane upgrades should also be automatically handled by your provider.&lt;/p&gt;

&lt;p&gt;Even with a hosted service, you are still responsible to verify workloads, additional controllers, and third-party plugins (such as CNI) that you have installed in your cluster. Those components should be tested for API compatibility before you upgrade your cluster in a test or development environment. We talked about making sure your workloads and controllers are compatible with different Kubernetes API versions in a previous blog post.&lt;/p&gt;

&lt;p&gt;With any Kubernetes upgrade, you should upgrade components in the following order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Control plane&lt;/li&gt;
&lt;li&gt;Data plane and nodes&lt;/li&gt;
&lt;li&gt;Add-ons&lt;/li&gt;
&lt;li&gt;Workloads&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These upgrade patterns will help you decide how to upgrade those components that best suit your cluster and environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  In-Place Upgrades
&lt;/h2&gt;

&lt;p&gt;When performing an in-place upgrade, you must be extra careful that components remain healthy, because you are performing work on a cluster currently serving production traffic. In-place upgrades can consist of package updates (e.g., yum, apt), config management automation (e.g., Ansible, Chef), or VM/container image changes. Ideally, your upgrade will be scripted and automated — including roll-back — but if this is your first time upgrading, doing it manually in a development or test environment may be helpful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2Fccd3ce1e-figure1-aa_in-place-upgrade-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2Fccd3ce1e-figure1-aa_in-place-upgrade-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In-place upgrades mean that all the components will upgrade at roughly the same time. If you change your desired API server version with configuration management and push a new configuration, all of the API servers will upgrade once they receive the new configuration. This is different from rolling upgrades, which we discuss later.&lt;/p&gt;

&lt;p&gt;The main benefits of in-place upgrades are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is the fastest to perform at any scale.&lt;/li&gt;
&lt;li&gt;If done manually, it can allow for more control of the components and upgrade process.&lt;/li&gt;
&lt;li&gt;It is easily suited for multiple environments (on-prem or cloud).&lt;/li&gt;
&lt;li&gt;It is the cheapest from an infrastructure cost perspective.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on your process, scale and tooling, an in-place upgrade is probably the most straightforward approach to be able to script and roll out. Scripts can be tested locally or in development clusters, without needing to reconfigure resources that cluster administrator teams may not have access to — such as load balancers or DNS.&lt;/p&gt;

&lt;p&gt;In-place upgrades also have the following restrictions to consider if you want to use this method for upgrades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is possible to cause downtime if all of your API servers or controllers upgrade at the same time.&lt;/li&gt;
&lt;li&gt;If you want to move from Kubernetes 1.16 to 1.20, you’ll have to upgrade the entire cluster four times to each minor version.&lt;/li&gt;
&lt;li&gt;Validating each step may be a manual process, which can add additional time and opportunity for mistakes.&lt;/li&gt;
&lt;li&gt;You should test a rollback plan in case of failure because some upgrades cannot be easily reverted. (e.g., scheme changes).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Blue/Green Upgrades
&lt;/h2&gt;

&lt;p&gt;Blue/green cluster upgrades will require you to create a second cluster with a new version of Kubernetes. You will need to deploy a new control plane and data plane, then copy all of your workloads to the new cluster before you switch traffic from the old cluster to the new one. You can use blue/green to update each component of your cluster, but a holistic cluster upgrade is easier to deploy and rollback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2Feaf3dee5-figure2-aa_blue-green-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2Feaf3dee5-figure2-aa_blue-green-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The good news is, setting up new clusters is generally easier than upgrading a cluster in place. You have multiple options on how you deploy workloads to the new cluster. If your workloads are already part of a GitOps or continuous delivery, you can have deployments go to the new cluster simultaneously to the old cluster, prior to or during your upgrade. If you do not have deployments automated, you use a tool like Velero to back up your existing workloads and deploy them to the new cluster.&lt;/p&gt;

&lt;p&gt;Creating a new “green” cluster can give you a lot of confidence that the new version works as you expect and puts you in control over when you switch versions. The new cluster can also be used to validate automation tooling, such as Terraform modules or GitOps repos. You can make the change via DNS or load balancers whenever you are ready, or even during a maintenance window or low utilization time.&lt;/p&gt;

&lt;p&gt;Key benefits of a blue/green upgrade are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-validate that all components are healthy before sending traffic.&lt;/li&gt;
&lt;li&gt;You can upgrade multiple versions at a time (e.g., 1.16 directly to 1.20).&lt;/li&gt;
&lt;li&gt;You can change other parts of the infrastructure that might be difficult to test (e.g., switch regions, add zones, change instance types).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is the safest and easiest to roll back.&lt;br&gt;
Downsides for blue/green deployments to consider include:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This is the most expensive strategy in infrastructure cost, because you have to run twice the compute capacity during the migration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You may not be able to get all of the compute capacity you need to run a complete second cluster if you have thousands of worker nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This strategy is hard to scale to dozens or hundreds of clusters if you have multiple concurrent cluster upgrades.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Blue/green is not easy to do on-prem without virtualization, unless you have spare servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Switching all traffic at once may not be easy if you have lots of endpoints to update. Load balancers may need to be pre-scaled and caches warmed. Beware of DNS time to live (TTL) which may or may not work for spreading load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Switching all cluster traffic at once requires cross-team coordination to migrate to new clusters; as well as engineering cycles to verify that workloads are the correct scale.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Blue/green can be a great strategy when you have a smaller number of clusters or fewer than a couple of hundred worker nodes. It allows you to skip versions and it is safe for rollbacks, but beware how much it may cost you in infrastructure spend and coordination time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rolling Upgrades
&lt;/h2&gt;

&lt;p&gt;If you’re familiar with Kubernetes deployment strategies, you’ll be familiar with rolling upgrades. A rolling upgrade will deploy one new copy of a component and then scale down one old copy. It will continue this pattern until all of the old components have been removed. The incremental nature of rolling upgrades has some advantages over in-place and blue/green strategies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2Fe9039151-figure3-aa_rolling-upgrade-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2Fe9039151-figure3-aa_rolling-upgrade-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to in-place upgrades, you’ll need to upgrade one minor version of Kubernetes at a time. This can be additional work when needing to upgrade multiple versions, but it’s the only supported option. Depending on the component you’re upgrading, you may use different tools to upgrade each.&lt;/p&gt;

&lt;p&gt;For resources like the control plane, you may want to add a new server with an upgraded API server to the control plane and then shut down an old server. If you’re in AWS, you could change an Auto Scaling group launch configuration AMI and replace one instance at a time. Other control plane components (e.g., scheduler) may be running as containers inside your cluster, so you can upgrade those with standard Kubernetes rolling deployment upgrades.&lt;/p&gt;

&lt;p&gt;The key difference in a rolling upgrade compared to blue/green is that your external traffic routing (DNS and load balancers) will stay pointed to the same place. You will want to make sure you test all add-ons and workloads in a different cluster or environment before you move forward with a production cluster upgrade.&lt;/p&gt;

&lt;p&gt;Note that AWS managed node groups, kOps, Cluster-API and many other Kubernetes cluster management tools use a rolling upgrade strategy. Benefits include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safer roll-outs and roll back compared to in-place upgrades.&lt;/li&gt;
&lt;li&gt;Costs less than blue/green and less likely to run out of resources.&lt;/li&gt;
&lt;li&gt;Can be paused mid-upgrade if something breaks.&lt;/li&gt;
&lt;li&gt;Can be adapted to on-prem environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rolling upgrades are the most common for automated tools. They have a good balance between speed and cost, and even though they’re not fully immutable, they’re still immutable in the right areas to reduce manual work and risk.&lt;/p&gt;

&lt;p&gt;When upgrading a production cluster, all of your existing workloads will still be deployed; and as long as you’ve tested their compatibility, your upgrades should be automatable.&lt;/p&gt;

&lt;p&gt;Further considerations when using rolling upgrades include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rolling upgrades can be slow depending on your scale.&lt;/li&gt;
&lt;li&gt;You may need to coordinate controller, daemonset, or plugin upgrades during the rollout.&lt;/li&gt;
&lt;li&gt;You might not be able to make cluster-wide changes, such as adding an availability zone or changing architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Canary Upgrades
&lt;/h2&gt;

&lt;p&gt;Canary application deployments serve small increments of traffic to a new version of an application at a time. Canary upgrades can be thought of as rolling upgrades with blue/green benefits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2F962e4b67-figure4-aa_canary-upgrade-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.thenewstack.io%2Fmedia%2F2021%2F04%2F962e4b67-figure4-aa_canary-upgrade-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With canary upgrades, you will create a new Kubernetes cluster with the version you want to deploy. Then add a small data plane and deploy your existing applications at smaller scales to the new cluster. Add the new cluster workloads to your existing production traffic via your load balancer configuration, DNS round-robin, or service mesh.&lt;/p&gt;

&lt;p&gt;Now you can monitor traffic going to the new cluster, slowly scale up workloads in the new cluster and scale down workloads in the old cluster. You can do this one workload at a time and as slowly or quickly as you’re comfortable with. If any individual workload starts to get errors, you can scale down the individual workload in the new cluster to have it automatically use the old cluster.&lt;/p&gt;

&lt;p&gt;The benefits to canary cluster upgrades include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New clusters are easier to create and validate.&lt;/li&gt;
&lt;li&gt;You can skip minor Kubernetes versions during upgrades (e.g., 1.16 to 1.20).&lt;/li&gt;
&lt;li&gt;Application deployments can be opt-in on a per-team basis.&lt;/li&gt;
&lt;li&gt;Errors are minimally impacting due to incremental traffic usage.&lt;/li&gt;
&lt;li&gt;You can make large infrastructure changes during upgrades.&lt;/li&gt;
&lt;li&gt;Clusters start small and scale, so infrastructure costs are lower and you can warm caches and load balancers as you scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to make large changes (such as changing architecture) or you want to add an additional availability zone, then canary is a great option. By starting the cluster small and growing it per workload, you can make sure that you’re not over-provisioning infrastructure if your new instances are more efficient or workload requests and limits have changed.&lt;/p&gt;

&lt;p&gt;As with anything, there are trade-offs. When using canary deployments, you should be aware of some of the following concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rolling back an application may require manual intervention to change a load balancer or scale down the new cluster.&lt;/li&gt;
&lt;li&gt;You may end up with an old cluster longer than you want, as applications slowly deploy and scale-up.&lt;/li&gt;
&lt;li&gt;Debugging applications can be harder because you need to know which cluster errors are happening.&lt;/li&gt;
&lt;li&gt;If you have dozens or hundreds of clusters, you will likely increase your cluster count by 50% or more as clusters are being upgraded.&lt;/li&gt;
&lt;li&gt;Canary is the most complex upgrade strategy, but it benefits from automated deployments, health checks, and performance monitoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;No matter which upgrade strategy you pick, it’s important to know how they work and any possible concerns as your Kubernetes usage grows. You need to have an upgrade strategy because Kubernetes has frequent releases and (like any software) occasional bugs.&lt;/p&gt;

&lt;p&gt;Keeping up to date with new versions can be an important part of your infrastructure security process and will enable applications to take advantage of new features quickly. If you deployed Kubernetes and migrated all of your workloads without considering how you would upgrade, now is the perfect time to start planning.&lt;/p&gt;

&lt;p&gt;If you do not have a business need to run your own Kubernetes clusters, I highly recommend you use one of the hosted Kubernetes options available. Opting into a managed control plane and data plane can save you days or weeks of planning and upgrades each year. Each hosted option may perform upgrades differently, but they will all allow you to focus on your workloads and business value instead of control plane high availability or data plane compatibility.&lt;/p&gt;

</description>
      <category>kubernets</category>
    </item>
    <item>
      <title>The Document Culture of Amazon</title>
      <dc:creator>Justin Garrison</dc:creator>
      <pubDate>Mon, 15 Mar 2021 17:30:29 +0000</pubDate>
      <link>https://forem.com/rothgar/the-document-culture-of-amazon-574c</link>
      <guid>https://forem.com/rothgar/the-document-culture-of-amazon-574c</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--urSCdDXA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v01jhptpbddgovyqud7i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--urSCdDXA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v01jhptpbddgovyqud7i.jpg" alt="picture of paper, pens, and phone" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my time at Amazon, I've observed the way we use documents is incredibly unique.&lt;br&gt;
A lot has been written about the six-pager and PR/FAQ so I'm not going focus on document formats, but I wanted to share how our process benefits from document-based meetings.&lt;br&gt;
I also have identified some areas for improvement if you are looking to adopt document-based meetings for your workplace.&lt;/p&gt;

&lt;p&gt;I've been wanting to write this blog post for a while and Jaana's tweet finally gave me the inspiration to share my thoughts.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--n9j2wKhL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1343095850768035841/lXTTjc0__normal.jpg" alt="Jaana Dogan ヤナ ドガン profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Jaana Dogan ヤナ ドガン
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/rakyll"&gt;@rakyll&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Meetings starting with silent document reading is the best idea ever. I have no idea why this isn't common practice in the industry.
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      20:20 PM - 10 Mar 2021
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1369744993099780096" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1369744993099780096" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1369744993099780096" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;My role is a Sr. Developer Advocate within AWS Container Services so my experience may be different from other areas and roles at Amazon.&lt;br&gt;
A majority of my meetings start with reading a document.&lt;br&gt;
This takes "this meeting could have been..." and flips it upside down.&lt;br&gt;
Most of the time, if there isn't a doc there isn't a meeting.&lt;/p&gt;

&lt;p&gt;Depending on the meeting, the document could be a six-pager, a PR/FAQ, a one-pager about an idea, a narrative to help find a solution to a problem, or even a service review full of charts, graphs and bullet points.&lt;br&gt;
The document adapts to fit the audience and purpose of the meeting.&lt;/p&gt;

&lt;p&gt;Reading documents is so ingrained in our culture and process that our scheduling tools have checkboxes to automatically create a document.&lt;br&gt;
If I'm catching up on a new service or feature launch, I will find the document rather than emailing or calling the product manager.&lt;/p&gt;

&lt;p&gt;The interesting part to me isn't in the format of the document, but how it is used.&lt;br&gt;
Meetings start with reading.&lt;br&gt;
Depending on the length of the document, we'll read anywhere from ten minutes to half an hour.&lt;br&gt;
If the meeting has a long document (six-pagers are the longest) and many attendees, the meeting will be scheduled for enough time to read and discuss.&lt;/p&gt;

&lt;p&gt;Reading the doc is part of the scheduled time.&lt;br&gt;
I've worked at plenty of places that I've tried to document everything for a meeting ahead of time.&lt;br&gt;
I've written well thought-out emails, shared links to documents, and written detailed wiki pages.&lt;br&gt;
In all of my meetings outside of Amazon I had one of three outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No one read the email/document and I had to explain everything in the meeting&lt;/li&gt;
&lt;li&gt;Some people read the document but forgot what it said because they read it days or hours before&lt;/li&gt;
&lt;li&gt;At least one person had a question that I could have answered via email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before I started at Amazon these were some of the expected benefits to reading in meetings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All of the information for the meeting is contained in the doc&lt;/li&gt;
&lt;li&gt;People are not expected to find their own time to read&lt;/li&gt;
&lt;li&gt;The document is fresh in everyone's mind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some other benefits I've found while putting it in practice are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Presenters, including myself, don't have to feel nervous about presenting in front of people.&lt;/li&gt;
&lt;li&gt;Documents help eliminate many biases, for or against, the person who wrote the document.&lt;/li&gt;
&lt;li&gt;You read everything in your own voice and vocal communication barriers such as accents, vocal ticks, or handicaps (e.g. stuttering or hearing loss) are not present in the document.&lt;/li&gt;
&lt;li&gt;There's no "can you see my screen", background noise, or call audio disconnects while understanding the main content for the meeting.&lt;/li&gt;
&lt;li&gt;There's a wealth of current and past documents. Want to see what the PR/FAQ looked like for Amazon EKS or AWS Lambda? It exists.&lt;/li&gt;
&lt;li&gt;You are not expected to retain document information outside of the meeting. Feedback and discussions happen during the meeting. Comments can be answered asynchronously. If more discussion is needed then the document will be revised and a new meeting will be scheduled to read and discuss it.&lt;/li&gt;
&lt;li&gt;If a document is provided early and I have a conflict at the begining of the meeting I can read the document ahead of time and join the meeting 10-20 minutes late without missing anything.&lt;/li&gt;
&lt;li&gt;Document reading from home has been a great way to get 10-20 minutes of exercise in.&lt;/li&gt;
&lt;/ul&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8hw-dSR2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/Eryi-OPVQAAh-2G.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--JOLRQs2s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1335993178256601088/qGeS1IAa_normal.jpg" alt="Justin Garrison profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Justin Garrison
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/rothgar"&gt;@rothgar&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Reading docs at the beginning of meetings is a great way to squeeze in some exercise!&lt;br&gt;&lt;br&gt;I'm trying to make this a new habit. Even a 10 minute climb is enough to get my heart rate up.&lt;br&gt;&lt;br&gt;Pay no attention to the cables 🙈 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      17:30 PM - 15 Jan 2021
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1350133181316431872" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1350133181316431872" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1350133181316431872" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;There are some limitations to document based meetings.&lt;br&gt;
The first I have noticed is if you're not a very good writer you will be at a disadvantage communicating your ideas.&lt;br&gt;
Amazon has a lot of internal training to help you write good Amazon documents, but even with training it still requires a good foundation.&lt;br&gt;
I'm very thankful for my years of practice writing for &lt;a href="https://www.howtogeek.com/author/rothgar/"&gt;How-To Geek&lt;/a&gt; and &lt;em&gt;&lt;a href="https://www.cnibook.info/"&gt;Cloud Native Infrastructure&lt;/a&gt;&lt;/em&gt;.&lt;br&gt;
Even with that background, my first one-pager review was intimidating.&lt;/p&gt;

&lt;p&gt;I've been told "if there's no document, it doesn't get done."&lt;br&gt;
This includes meetings.&lt;br&gt;
If there's no document, we cancel the meeting.&lt;/p&gt;

&lt;p&gt;While documents can create a more level playing field it's also a high barrier to entry.&lt;br&gt;
Even for small ideas, features, or iterations the first thing you will likely need is a document.&lt;br&gt;
My perspective may be skewed because in my current role I don't write production service code, but I do affect features, design, and positioning of the services.&lt;br&gt;
I have a lot of feedback and ideas, but I don't implement them.&lt;/p&gt;

&lt;p&gt;The available wealth of historical documents can be enlightening to read through, but it also can be confusing to trace the lineage of a service with a plethora of documents and comments.&lt;br&gt;
I have caught myself more than once reading a document which I thought sounded similar to an existing service only to realize 20 minutes later the document &lt;em&gt;is&lt;/em&gt; for the service and I didn't look at the date of the document or I didn't know the product code name.&lt;/p&gt;

&lt;p&gt;I have the benefit of working with the service teams and providing direct feedback about new features and services.&lt;br&gt;
Most documents I read are very interesting.&lt;br&gt;
Many teams at Amazon don't have direct product input.&lt;br&gt;
However, they're still required to write documents to communicate plans with their team.&lt;br&gt;
Not all docs are interesting, but they are crucial to the decision making process.&lt;/p&gt;

&lt;p&gt;I've said multiple times that Amazon has the foundation for a great remote-first company.&lt;br&gt;
The document-based process provides employees in multiple timezones the same context as someone in Seattle.&lt;br&gt;
The discussions in meetings enable faster feedback loops, but the downside is they can limit the potential for asynchronus engagements if notes and questions aren't captured in the document.&lt;/p&gt;

&lt;p&gt;I've never had the benefit of attending an Amazon meeting in person.&lt;br&gt;
From what I've heard document-centered meetings have been extremely successful in transitioning to remote requirements.&lt;br&gt;
But the content isn't the only thing that matters.&lt;/p&gt;

&lt;p&gt;Amazon, like many large organizations, uses a lot of tools to communicate.&lt;br&gt;
It's often difficult to find documents spread across so many tools without an ability to cross search or centrally organize them.&lt;br&gt;
I could see how a tool like &lt;a href="https://getcommande.com/"&gt;Command E&lt;/a&gt; could help a lot.&lt;br&gt;
This can be additionally difficult with the multitude of open source and social platforms I engage with on a weekly basis.&lt;br&gt;
I currently have a user in 53 Slack workspaces. 💀&lt;/p&gt;

&lt;p&gt;Even with areas that could use improvement I can't think of a place I have worked that wouldn't benefit from document-based meetings.&lt;br&gt;
I'm sure with more practice I will get better at writing with a more Amazonian style.&lt;/p&gt;

&lt;p&gt;I thoroughly enjoy not having to prep for meetings, because I'm confident the document will give me the context I need to participate.&lt;br&gt;
I know the person who called the meeting invested their time so they don't waste mine.&lt;br&gt;
I recipricate the same thoughtfulness with meetings I create and documents I write.&lt;/p&gt;




&lt;p&gt;Thank you everyone who reviewed this document. ☺️&lt;br&gt;
Banner image credit &lt;a href="https://pixabay.com/illustrations/paper-messy-notes-abstract-3033204/"&gt;pixbay&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Getting started with signal</title>
      <dc:creator>Justin Garrison</dc:creator>
      <pubDate>Fri, 29 Jan 2021 07:08:04 +0000</pubDate>
      <link>https://forem.com/rothgar/getting-started-with-signal-21l0</link>
      <guid>https://forem.com/rothgar/getting-started-with-signal-21l0</guid>
      <description>&lt;p&gt;You've discovered that &lt;a href="https://signal.org/"&gt;Signal&lt;/a&gt; can provide you with secure communication from your phone that is cross platform and free!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l6sgP9EI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4m1267heu82pt8lr2mls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l6sgP9EI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4m1267heu82pt8lr2mls.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all you may be asking "who makes Signal?"&lt;br&gt;
Signal Messenger LLC. is the company that makes the Signal app.&lt;br&gt;
They are funded by the Signal Foundation which is a non-profit organization that relies on &lt;a href="https://signal.org/donate/"&gt;your donations&lt;/a&gt; to make the app free and not have ulterior motives with your data.&lt;/p&gt;

&lt;p&gt;The foundation's mission is&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To develop open source privacy technology that protects free expression and enables secure global communication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Signal app is all &lt;a href="https://github.com/signalapp"&gt;open source on GitHub&lt;/a&gt; and their protocols are well documented and available &lt;a href="https://signal.org/docs/"&gt;on their website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But that's not why you're here.&lt;br&gt;
Let's show you some tips to get the most out of your new secure messaging app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important settings
&lt;/h2&gt;

&lt;p&gt;Signal is great, but there are a few things you may want to change depending on how secure or private you want your communications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notifications.
&lt;/h3&gt;

&lt;p&gt;You can disable what is shown in notifications to help with privacy.&lt;br&gt;
Turn off whatever you don't want shown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--koJMz7rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6kylixcjmbrzz512li7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--koJMz7rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6kylixcjmbrzz512li7l.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may also want to disable the notification you get when one of your contacts join Signal.&lt;br&gt;
Especially ad the rapid pace people are joining.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vfwEpzHn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h33eth9vkn7chinq07qt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vfwEpzHn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h33eth9vkn7chinq07qt.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy
&lt;/h3&gt;

&lt;p&gt;You can set the Signal app to lock when your phone locks under privacy.&lt;br&gt;
It may also be a good idea to enable incognito keyboard which will ask your keyboard to remove personalization while typing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AyEmz_D7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f5xvm1ke5tmpbeu5j5am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AyEmz_D7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f5xvm1ke5tmpbeu5j5am.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can turn off read receipts, typing indicators, and automatic link previews under the communication settings.&lt;br&gt;
Link previews only work on &lt;a href="https://support.signal.org/hc/en-us/articles/360022474332-Link-Previews"&gt;sites that use &lt;code&gt;https://&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aPF0_uU0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cssd7uo1qcc2ap1rk2eb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aPF0_uU0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cssd7uo1qcc2ap1rk2eb.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Chats and media
&lt;/h3&gt;

&lt;p&gt;In chats you can change when to auto-download files.&lt;br&gt;
Some of these can be quite large so it may be a good idea to only automatically download on Wi-Fi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c7-N1CwN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/u2ogpdaz16czkod5dbx2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c7-N1CwN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/u2ogpdaz16czkod5dbx2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the very bottom of Chats and media is a &lt;a href="https://support.signal.org/hc/en-us/articles/360007059752-Backup-and-Restore-Messages"&gt;message backup option&lt;/a&gt;.&lt;br&gt;
The backup is encrypted and stored locally on your phone.&lt;br&gt;
If you're using a backup service for your phone they will also be backed up to the cloud which you may or may not want (even if it's encrypted).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ts9jD9E---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fh4jrrxli7sukah5enj4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ts9jD9E---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fh4jrrxli7sukah5enj4.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Linked devices
&lt;/h3&gt;

&lt;p&gt;You can use Signal from another device including tablets and laptops.&lt;br&gt;
Install the Signal app on the other device and it will show a QR code.&lt;br&gt;
Open the linked devices setting and click the + button at the bottom.&lt;br&gt;
Scan the QR code and you're all set.&lt;/p&gt;

&lt;p&gt;If you tap on an existing linked device you have the option to remove it from being able to send or receive messages.&lt;br&gt;
Be aware that any existing messages on the device may still be available and cached on the device.&lt;br&gt;
This setting is not a "secure wipe."&lt;/p&gt;

&lt;h2&gt;
  
  
  Chats and calls
&lt;/h2&gt;

&lt;p&gt;Signal chats work similarly to traditional SMS messaging apps.&lt;br&gt;
You can send text, emoji, and stickers, or attach pictures, gifs (searched from giphy), files, contacts, or locations.&lt;br&gt;
You can record voice messages by holding the microphone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xt9BKlp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j7w4uyhlvu0pius2snun.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xt9BKlp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j7w4uyhlvu0pius2snun.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also make audio or video calls when you select a contact and click the icons in the top banner.&lt;br&gt;
All calls are encrypted and can optionally go through a relay server so you do not expose your IP address to the recipient.&lt;br&gt;
You can enable the relay server in settings -&amp;gt; privacy -&amp;gt; communication.&lt;/p&gt;

&lt;p&gt;A relay is a server that Signal controls that your call will go through.&lt;br&gt;
Similar to a VPN in the way it would expose the server IP to the recipient without exposing your IP.&lt;br&gt;
If you're interested in the Signal server architecture you can check out &lt;a href="https://sorincocorada.ro/signal-messanger-architecture/"&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--huSOWkzx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cxbwc8wf16wyvqhg3qoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--huSOWkzx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cxbwc8wf16wyvqhg3qoy.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A single hallow check box next to a message you sent means your message was sent.&lt;br&gt;
Two check marks means the message was delivered.&lt;br&gt;
Once the recipient reads the message the check marks will fill in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jO_dcR0c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/60qolq4mt9vyyx72migh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jO_dcR0c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/60qolq4mt9vyyx72migh.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1hzNPkSN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vvro5tfzggmfn6u75g74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1hzNPkSN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vvro5tfzggmfn6u75g74.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you see a little person icon next to the person's name in chat it means that person is in your contacts.&lt;br&gt;
If you want their name to automatically be filled out you can go into your phone's contacts and add the number to a contact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N9p8HKf4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hf7n9cpy7kpcyr8np44v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N9p8HKf4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hf7n9cpy7kpcyr8np44v.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you open a chat you can look in settings and enable disappearing messages.&lt;br&gt;
This is super helpful to prevent messages persisting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G_h2JLPZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/un4sye20ibtawxubj3y9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G_h2JLPZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/un4sye20ibtawxubj3y9.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can—and should—verify a contact to make sure you're really talking to them.&lt;br&gt;
This verifies the encryption keys used at both ends of the conversation.&lt;br&gt;
You can do that manually or automatically by scanning their QR code.&lt;/p&gt;

&lt;p&gt;Ideally you will do this verification out-of-band from Signal either in person or through some other video/image sharing service.&lt;br&gt;
Facetime, zoom, etc. are great options if you can't meet in person.&lt;/p&gt;

&lt;p&gt;Once you verify a contact you'll see "Verified" text under their name.&lt;br&gt;
If you manually mark the contact as verified it will show a message in the chat that you marked them verified.&lt;/p&gt;

&lt;h3&gt;
  
  
  Group messages
&lt;/h3&gt;

&lt;p&gt;You can create a group message from the main screen clicking the pencil and then "New group."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---l044HJO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/frdrvjf3kawzv7oycxzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---l044HJO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/frdrvjf3kawzv7oycxzl.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add contacts to the group and you're ready to go.&lt;/p&gt;

&lt;p&gt;You have similar settings to 1:1 chats.&lt;br&gt;
You can have a group video call with up to 8 people and a group chat with up to 150 people—RIP notifications!&lt;br&gt;
If you want to have a group call without video you need to start a video call and disable your camera before you join.&lt;/p&gt;

&lt;p&gt;You can change who can invite people to the group or make a group link easier for someone to share if they're not in your contacts.&lt;/p&gt;

&lt;p&gt;The links are public but an admin needs to approve members joining the group by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j_X8-yx1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dam4njp2nffw73fi9im2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j_X8-yx1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dam4njp2nffw73fi9im2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Group video calls work the same way as 1:1 calls but groups don't have audio only calls—although you can disable your video in a video call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other things to consider
&lt;/h2&gt;

&lt;p&gt;If you want to use signal without giving out your real phone number you can use a service like twilio that will provide you a forwarding number.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://mshelton.medium.com/using-signal-without-giving-your-phone-number-3a575580f652"&gt;this great guide&lt;/a&gt; on how to set it up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your safety number has changed
&lt;/h3&gt;

&lt;p&gt;If you see this seemingly scary message don't be alarmed.&lt;br&gt;
It likely just means the &lt;a href="https://support.signal.org/hc/en-us/articles/360007060632-What-is-a-safety-number-and-why-do-I-see-that-it-changed-"&gt;person on the other end of the conversation&lt;/a&gt; got a new phone or re-installed Signal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mUpA_BJj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uf66j6mpw4k76eebfx9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mUpA_BJj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uf66j6mpw4k76eebfx9u.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can still verify the persons safety number to make sure there is no man-in-the-middle attacks.&lt;br&gt;
This is distinctly different than how iMessage and Telegram work because in both of those apps they store your private key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pins
&lt;/h3&gt;

&lt;p&gt;Pins are important because they will enable Signal to get rid of phone number requirements.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://support.signal.org/hc/en-us/articles/360007059792-Signal-PIN"&gt;support page&lt;/a&gt; says it best&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your Signal PIN is a code used to support features like non-phone number based identifiers. This means that your PIN can recover your profile, settings, contacts, and who you’ve blocked if you ever lose or switch devices. A PIN can also serve as an optional registration lock to prevent others from registering your number on your behalf.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It hasn't rolled out yet but has been in the works for a while.&lt;/p&gt;

&lt;p&gt;If you want to stay up to date on signal news I highly recommend you follow the &lt;a href="https://signal.org/blog/"&gt;Signal blog&lt;/a&gt;.&lt;br&gt;
They have lots of great technical and non-technical information on new features and news.&lt;/p&gt;

</description>
      <category>signal</category>
      <category>security</category>
      <category>communication</category>
    </item>
  </channel>
</rss>
