<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://shomil.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://shomil.me/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2024-12-16T06:56:01+00:00</updated><id>https://shomil.me/feed.xml</id><title type="html">Shomil Jain</title><subtitle>UC Berkeley | Electrical Engineering and Computer Science | Class of 2022</subtitle><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><entry><title type="html">Reverse Engineering BeReal</title><link href="https://shomil.me/bereal/" rel="alternate" type="text/html" title="Reverse Engineering BeReal" /><published>2022-03-31T00:00:00+00:00</published><updated>2022-03-31T00:00:00+00:00</updated><id>https://shomil.me/bereal</id><content type="html" xml:base="https://shomil.me/bereal/">&lt;p&gt;Earlier this month, I was walking back from class with one of my friends when she recieved a notification on her phone: “Time to BeReal!” She quickly opened up the app that sent it, and subsequently took a front-facing photo of where we were and motioned to me to be part of a selfie of the two of us. Two seconds later, she clicked “post.”&lt;/p&gt;

&lt;p&gt;I asked her what I had just taken part of, and she described to me how this new, viral social media app, &lt;a href=&quot;https://bere.al/en&quot;&gt;BeReal&lt;/a&gt;, worked. Here’s an introduction to the app from &lt;a href=&quot;https://www.bloomberg.com/news/articles/2022-03-29/bereal-the-un-instagram-is-trending-at-college-campuses-by-keeping-it-casual?sref=CrGXSfHu&quot;&gt;Bloomberg&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;At a varying time each day, BeReal users receive a single notification en masse prompting them to take two photos — simultaneous images, shot through the front- and back-facing lens on their phone cameras. Everyone is given two minutes to take the photographs, which are then shared with their followers on the app. People who miss the daily notification can post late, but such images are conspicuously marked for being tardy. To see everyone else’s contributions, a user must first upload their own daily shots.&lt;/p&gt;

  &lt;p&gt;There are no filters or “likes.” The underlying idea is that by giving users a short deadline, there’s little time for anyone to snap the perfect image. People can pose or quickly fix their hair, but more often than not users are compelled to share whatever is going on in their lives at that given moment, no matter how prosaic or unglamorous. On the app store, BeReal greets new users with a concise motto of sorts: “BeReal is life, Real life, and this life is without filters.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m naturally suspicious of social media apps - so I told my friend this on the spot, and she responded with something along the lines of: “What do I have to lose? I’m already on Snapchat and Instagram and Facebook – why not be a part of this other platform too?”&lt;/p&gt;

&lt;p&gt;And so I started to do some research. Here are some things that I found –&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The app (and company behind it) seemed to be in its early stages. The features were barebones (but functional), and the &lt;a href=&quot;https://bere.al/en&quot;&gt;website&lt;/a&gt; was minimal.&lt;/li&gt;
  &lt;li&gt;It closed a $30M (!!) funding round &lt;a href=&quot;https://nordic9.com/news/bereal-closed-a-30-million-capital-funding-round-backed-by-a16z-dst-accel-and-kima-ventures/&quot;&gt;backed by Andreessen Horowitz (a16z) and Accel&lt;/a&gt; in June 2021. Back then, it had ~300K MAU, mostly in France.&lt;/li&gt;
  &lt;li&gt;The earliest mention of it in “the real world” I could find in my two minutes of scavenging was a reference to it in the Georgetown University’s student newspaper in November 2021 - interestingly enough, it was an &lt;a href=&quot;https://thehoya.com/viewpoint-beware-of-bereal/&quot;&gt;anti-BeReal opinion article&lt;/a&gt;, which I found that I agreed with quite strongly.&lt;/li&gt;
  &lt;li&gt;It seemed to be going viral at Berkeley solely through word-of-mouth. I didn’t see or hear much about cross-posting (e.g. how Tiktok videos were quite often, and still are, cross-posted onto Instagram.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, naturally, I wanted to do a little bit of reverse engineering to see what exactly this $30M had backed! And so the rest of this article contains a walkthrough of my steps, thoughts, and takeaways from poking around BeReal using just an HTTP Request interceptor and a little bit of Python. Trivial reverse engineering, but quite revealing regardless.&lt;/p&gt;

&lt;p&gt;I’ve translated all client-side requests into the Python Requests language for readability and mutability. I’ve trimmed out some of the standard headers as well. I’ve also fuzzed the request/response data PII - e.g. phone numbers, names, UUID’s, etc. - to make sure none of them map to actual user data.&lt;/p&gt;

&lt;p&gt;And one final disclaimer: &lt;strong&gt;[1] I’m not a formal security engineer, so my poking around is from a software/architecture perspective, and [2] this is more of a privacy analysis than a security analysis.&lt;/strong&gt; Some of these things may be &lt;em&gt;secure&lt;/em&gt; by the application’s assumed threat model, but they may feel like a violation of the privacy that we expect from applications like these. I did not, at any point, “hack” BeReal, and do not intend to do so at any point in the future. My goal is purely pedalogical - to simply call out different privacy-oriented components of the design of a real-world application!&lt;/p&gt;

&lt;h1 id=&quot;the-scancontacts-endpoint&quot;&gt;The /scanContacts Endpoint&lt;/h1&gt;

&lt;p&gt;BeReal offers the ability to scan your contacts. When asking for this permission, the app claims that your contacts are not uploaded + stored on their servers. That said, here’s an interesting request the app makes on the “Add Friends” page.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Host'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'us-central1-alexisbarreyat-bereal.cloudfunctions.net'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Firebase-Instance-Id-Token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'User-Agent'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'AlexisBarreyat.BeReal/0.20.0 iPhone/15.4 hw/iPhone12_3'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Authorization'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Bearer ...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;json_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'data'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'phoneNumbers'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'+14087115196'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'+91540222551'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'+16103672510'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'+12253535340'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'+13588695809'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'https://us-central1-alexisbarreyat-bereal.cloudfunctions.net/scanContacts'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and here’s what the response looks like:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Albert Ghar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;phoneNumber&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;+14087115196&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;photoURL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Photos/L9Mk98Ftr3bskYPFEttXje9wPlH2/profile/L9Mm98Ftg3bskYPFEttXje9wPlH2-1646543857-profile-picture.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;L9Mm98Ftr3bgkYPFEttXje9wPlH2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;albertghar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Steve Smith&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;phoneNumber&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;+16103672510&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;photoURL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Photos/VLECGzDHUhThPmnv0LubFHDGEKR2/profile/VLVCGzDHUhThPmnv0LubFHDGEKR2-1643954998-profile-picture.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;VLVCGzDHUhAhPmnv0LubFHDGEKR2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stevesmith&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;discussion&quot;&gt;Discussion&lt;/h4&gt;

&lt;p&gt;I’m a little wary of this architecture. My contacts list has ~250 contacts, and the client made a request with ~250 raw phone numbers and, in a fraction of a second, spit out full name to phone number mappings for everyone who was on the platform, along with profile picture URL’s and user identifiers. It seems like this could be open to abuse: could one just pass in an arbitrary-length list of phone numbers (perhaps brute-forced), and, as long as the length of the list didn’t overflow the memory of whatever server is handling this endpoint, derive a list of all of the phone number &amp;lt;&amp;gt; username (and full name) mappings on this platform? That’s inherently bad architecture.&lt;/p&gt;

&lt;p&gt;I also don’t like the fact that the app claims that contacts are not stored on the server. If a request is being made with a bunch of phone numbers, that request is &lt;em&gt;probably&lt;/em&gt; getting logged somewhere, along with the authentication token of the user that made the request. Using this information, a BeReal engineer could presumably put together a dataset of users’ address books relatively easily.&lt;/p&gt;

&lt;p&gt;The core engineering problem being tackled here has a name: “contact discovery.” It’s pretty common among social media apps. Different apps handle this differently - e.g. Snapchat and Facebook upload and store entire address books to their servers, so this is certainly one step better than that. But it’s far from what I’d consider “good” design.&lt;/p&gt;

&lt;p&gt;The folks at Signal have an excellent blog post describing their approach to &lt;a href=&quot;https://signal.org/blog/private-contact-discovery/&quot;&gt;privacy preserving contact discovery&lt;/a&gt; - which is a method to allow Signal clients to efficiently and scalably determine whether the contacts in their address book are Signal users &lt;strong&gt;without revealing the contacts in their address book to the Signal service&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I don’t think BeReal needs to match Signal’s privacy standards - I think that’s an unreasonable demand. A “privacy preserving social media network” is a bit of an oxymoron - by signing up for this platform, you’re inherently volunteering &lt;em&gt;some&lt;/em&gt; of your data away. But there certainly must be smaller, more incremental improvements (e.g. better rate limiting, more granular opt-in/opt-out around contact disccovery) that could be made to limit abuse here.&lt;/p&gt;

&lt;h1 id=&quot;the-apifeedsdiscovery-endpoint&quot;&gt;The /api/feeds/discovery Endpoint&lt;/h1&gt;

&lt;p&gt;Here’s another interesting endpoint. The homepage of this app has a &lt;strong&gt;Discovery&lt;/strong&gt; tab, which allows you to discover the daily BeReal posts of users who choose to make their posts public. The endpoint used to query this information is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/feeds/discovery&lt;/code&gt; endpoint:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Host'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'mobile.bereal.com'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'User-Agent'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'BeReal/5063 CFNetwork/1331.0.7 Darwin/21.4.0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Authorization'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'limit'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'20'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'https://mobile.bereal.com/api/feeds/discovery'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and here’s the response:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;lastIndex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1648747266091-0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;posts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bucket&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;storage.bere.al&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;creationDate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_nanoseconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;846000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_seconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1648747267&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;NtLZ9OgIS0b2jdvy42Ob&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;imageHeight&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;imageWidth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;isPublic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;lateInSeconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1903&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_latitude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;44.913278&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_longitude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;-0.644326&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mediaType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;late&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;notificationID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;7k67KQCTgKD1Jiq_y6MDa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ownerID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;AbfqcmwUmAWiM1j4vI7NONAwCHs2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;photoURL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://storage.bere.al/Photos/AbfqcmwUmAWiM1j4vI7NONAwCHs2/bereal/17750a05-d675-41fe-a505-5378dfdb34fb-1648747262.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;realMojis&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_nanoseconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;244000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_seconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1648747279&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;emoji&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;😐&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;neutral&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;gMRPfgbganNKJu12RGOK1SKwCiD3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://storage.bere.al/Photos/gMRPfgbganNKJu12RGOK1SKwCiD3/realmoji/b85c9a9e-20c7-478c-8f94-7a874197aa58-vYU2ZFY3uiSDXCQB-neutral-1642222484.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                           &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;smallp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;region&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;us-central&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;secondaryImageHeight&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;secondaryImageWidth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;secondaryPhotoURL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://storage.bere.al/Photos/AbfqcmwUmAWiM1j4vI7NONAwCHs2/bereal/17750a05-d675-41fe-a505-5378dfdb34fb-1648747262-secondary.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;takenAt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_nanoseconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_seconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1648747262&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;updatedAt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1648747279304&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;gabipet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;discussion-1&quot;&gt;Discussion&lt;/h4&gt;

&lt;p&gt;Let’s break this down. In the response, the app serves a random list of ~20 BeReal posts (e.g. from 20 users who’ve chosen to make their posts public). The metadata for each of these posts contain –&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The post creation timestamp (and image capture timestamp), to high precision (nanoseconds)&lt;/li&gt;
  &lt;li&gt;The owner ID, which can be mapped into a full name using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/getUserNames&lt;/code&gt; endpoint&lt;/li&gt;
  &lt;li&gt;A list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;realMojis&lt;/code&gt;, which contain reactions to the post made by the user’s friends (and the friend UID’s, along with the nanoseconds at which those reactions occurred)&lt;/li&gt;
  &lt;li&gt;The photo URL as a direct storage bucket URL&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The latitude and longitude at which the post was taken&lt;/strong&gt;, with &lt;a href=&quot;https://stackoverflow.com/questions/1947481/how-many-significant-digits-should-i-store-in-my-database-for-a-gps-coordinate&quot;&gt;precision to one meter&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another interesting thing: if I replicate this query in Python, and re-run the cell containing the request over and over again, I get a new, random set of user posts each time.&lt;/p&gt;

&lt;p&gt;So I wrote a little bit of code to hit this API over and over again. Nothing excessive: just 10 times, to simulate swiping down and refreshing the page 10 times (which is totally, in my opinion, within reason).&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dataset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query_discovery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'posts'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'location'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dataset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'userName'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'location'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and here’s a sample of the dataset I collected in ~5 seconds. Again, I’ve randomly modified the usernames here (and locations) to protect user privacy. &lt;strong&gt;It took just 5 seconds to collect the precise latitude, longitude, and timestamp of ~150 BeReal users’ posts.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[['kaydne', {'_latitude': 40.017117, '_longitude': -105.28362}],
 ['cf14vc', {'_latitude': 43.70298, '_longitude': -72.290956}],
 ['raffh3', {'_latitude': 39.958509, '_longitude': -75.189487}],
 ['fatafa', {'_latitude': 43.138835, '_longitude': -80.266258}],
 ['maaiee', {'_latitude': 43.300878, '_longitude': -2.015236}],
 ['mefef5', {'_latitude': 48.106997, '_longitude': -1.689937}],
 ['ercten', {'_latitude': 48.147432, '_longitude': -0.391368}],
 ['cdan57', {'_latitude': 41.157043, '_longitude': -73.240702}],
 ['anaiff', {'_latitude': -32.371675, '_longitude': -54.164698}],
 ['holnss', {'_latitude': 50.772838, '_longitude': 0.264811}],
 ['nafhan', {'_latitude': 35.104979, '_longitude': -92.333383}],
 ['jaghss', {'_latitude': 42.733908, '_longitude': -84.483092}],
 ['loatli', {'_latitude': -34.434153, '_longitude': -58.594687}]
 ...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now one might argue that it’s OK that all of this data is public, since these users opted into making it public. So from a security perspective, this isn’t really all that interesting. But from a privacy perspective, providing this level of location/timestamp precision through an open API is worrying: this is sensitive user data! Why is it possible to extract so much user data with such ease? What if these locations map to people’s homes? Workplaces?&lt;/p&gt;

&lt;p&gt;To take this one step further, while I didn’t include the post photos themselves in my toy dataset above, I’d imagine one could glean some information from the photos themselves. Each post contains a direct &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;photoURL&lt;/code&gt; (back camera) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secondaryPhotoURL&lt;/code&gt; (front camera/selfie) – so it’d potentially be possible to extract even more information from the content contained within these photos (e.g. gender, ethnicity, socioeconomic status, etc.).&lt;/p&gt;

&lt;p&gt;So considering that there’s hundreds of thousands (millions?) of BeReal users with public profiles, there’s an incredible amount of “scrapable” data here. That’s honestly kind of terrifying. It doesn’t really make sense as to why this much metadata is made available through this endpoint.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I also tried bumping up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;limit&lt;/code&gt; parameter to ~40 instead of 20, and the response only returned 20. That’s good on BeReal’s part. Enforcing the limit per query on the backend is important.&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;the-friendsuggestions-endpoint&quot;&gt;The /friendSuggestions Endpoint&lt;/h1&gt;

&lt;p&gt;Ok, so this is the scariest section of all. Be warned.&lt;/p&gt;

&lt;p&gt;This app contains an &lt;strong&gt;“Add friends”&lt;/strong&gt; page. On this page, the client makes a request to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/friendSuggestions&lt;/code&gt; endpoint that looks like the following:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Host'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'us-central1-alexisbarreyat-bereal.cloudfunctions.net'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Firebase-Instance-Id-Token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'......'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'User-Agent'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'AlexisBarreyat.BeReal/0.20.0 iPhone/15.4 hw/iPhone12_3'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'Authorization'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Bearer .....'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;json_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;'data'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'friends'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'OaomHuKxYYMJimflUNbkm1rPXsy1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'toExclude'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'https://us-central1-alexisbarreyat-bereal.cloudfunctions.net/friendSuggestion'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and the response looks like the following:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;6xqLArEVbkM6WEwPpltM2k8ddJx2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stevesmith&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;countCommon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;commonFriends&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;OaomHuKxYEMJimflUNbkm1rPXsy1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;shomiljain&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;steve smith&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;photoURL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Photos/6xqLArQVbkMEWEwPpltM2k8ddJE2/profile/6xqLArQVbkM6WEwPpltM2k8ddJx2-1647191E36-profile-picture.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tD3qMoEfFHdMJVJqHIhT8moaMBl1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ironman&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;countCommon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;commonFriends&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;OaomHuKxYYMJGvflUNbkm1rPXsy1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;userName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;shomiljain&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Tony Stark&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;photoURL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Photos/tD3qMoXfFPdMJVJqHIhT8moEMBl1/profile/tD3qMoXfFPdMJVJqHIhT8moaMBl1-16486F2975-profile-picture.jpg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;discussion-2&quot;&gt;Discussion&lt;/h4&gt;

&lt;p&gt;Intuivitely, how would a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;friendSuggestions&lt;/code&gt; endpoint work? Well, given a list of your friends, it would tell you who your friends are friends with, sorted by the number of mutual friends you have with each of your friends’ friends. (That’s a bit of a toungue twister.)&lt;/p&gt;

&lt;p&gt;But here’s where things get interesting: if we only pass one friend into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;friends&lt;/code&gt; parameter of this request, then this basically reduces to just &lt;strong&gt;finding all of the friends of the given friend that we pass in&lt;/strong&gt;. (I confirmed this behavior by running this endpoint on a few of my friends - with permission, of course - and asking them to confirm the endpoint’s output was correct.)&lt;/p&gt;

&lt;p&gt;So what are the implications of this? Consider these ~10 lines of code I wrote.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;fringe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'2czmn8Ce1jA6ZHt0GUHWS25iWug1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'joshhug'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fringe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fringe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'exploring neighbor:'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_friend_suggestions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'result'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'uid'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'userName'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;fringe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’ve taken an introductory CS class, these lines of code should look familiar - this is BFS! We start by exploring a single user (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;joshug&lt;/code&gt;, here, who’s a hypothetical user) - and we work our way outwards in a breadth-first manner. Using this, we can pretty much..leak the entire BeReal network, assuming we run this code for long enough.&lt;/p&gt;

&lt;p&gt;As a proof of concept, I ran this code for ~1 minute, starting at my friend’s user ID (who goes to UC Berkeley). Here’s what the results look like –&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;2022-03-31-bereal.assets/image-20220331112755402.png&quot; alt=&quot;image-20220331112755402&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On this graph, each node is a user. Each edge is a “friend” relationship between two users.&lt;/p&gt;

&lt;p&gt;Now if each user had ~500-1000 friends (e.g. on a platform like Facebook), the results of this endpoint wouldn’t be all that exciting. There’d be a lot of noise; you’d be able to create some sort of social web, but it would be incredibly dense.&lt;/p&gt;

&lt;p&gt;But on BeReal, the app, by nature, is meant for just you and your &lt;strong&gt;close friends&lt;/strong&gt;. So inherently, you’re telling BeReal (and now, let’s say, &lt;em&gt;everyone&lt;/em&gt;) who your closest friends are. This is really important: it’s a bias built into the platform itself that amplifies the effect of this discovery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which means that it’s possible to discover the closest friends of pretty much every single user on this platform, including those that think they’re protecting their privacy by choosing to keep their posts private.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If I wanted to get super Black-Mirror-Esque, I could replace the little blue dots on these pictures with these user’s profile pictures and full names. And for those who opted into attaching their location to their posts (which, as I noted above, seems to be a non-trivial number of users), I could throw in a location dimension as well.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h1&gt;

&lt;p&gt;For people who’re going to be building systems that are used by many people at scale –&lt;/p&gt;

&lt;p&gt;Designing privacy-preserving software is hard. There’s a lot of focus on very specific parts of software security: preventing unauthorized users from accessing application data, or defending against code injection/cross-site scripting/path traverrsal attacks/memory safety attacks (where appropriate, of course). But sometimes the most obvious attacks aren’t related to these at all: they’re nuances in your software architecture that have significant privacy implications, even though your application may still be “secure” by technical standards.&lt;/p&gt;

&lt;p&gt;And more generally –&lt;/p&gt;

&lt;p&gt;It’s one thing if you’re giving your data to a company whose products and services you pay for (e.g. Apple). It’s another if you’re voluntarily trading your data in exchange for a free service (e.g. Facebook). But it’s an entirely different thing if you’re unknowingly making your data available to anyone who’s passed CS 61B @ UC Berkeley.&lt;/p&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="projects" /><category term="security" /><summary type="html">Earlier this month, I was walking back from class with one of my friends when she recieved a notification on her phone: “Time to BeReal!” She quickly opened up the app that sent it, and subsequently took a front-facing photo of where we were and motioned to me to be part of a selfie of the two of us. Two seconds later, she clicked “post.”</summary></entry><entry><title type="html">An automated extensions pipeline for large CS classes</title><link href="https://shomil.me/extensions/" rel="alternate" type="text/html" title="An automated extensions pipeline for large CS classes" /><published>2022-01-29T00:00:00+00:00</published><updated>2022-01-29T00:00:00+00:00</updated><id>https://shomil.me/extensions</id><content type="html" xml:base="https://shomil.me/extensions/">&lt;blockquote&gt;
  &lt;p&gt;This post is cross-posted from the README of this &lt;a href=&quot;https://github.com/cs161-staff/extensions&quot;&gt;project’s GitHub repository&lt;/a&gt;. It’s a work in progress, so I’d refer to the README for the latest documentation on how this works!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The CS 161 Extensions Pipeline is a lightweight framework designed for tracking, approving, and managing extension requests in medium and large classes (e.g. N &amp;gt; 50). It’s optimized for courses in the EECS department at UC Berkeley, but is extensible to other departments and universities.&lt;/p&gt;

&lt;p&gt;At a high level, this pipeline consists of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A &lt;strong&gt;&lt;a href=&quot;https://forms.gle/m25EswwXMpq6vqrp7&quot;&gt;Google Form&lt;/a&gt;&lt;/strong&gt; that students submit extension requests to.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;&lt;a href=&quot;https://docs.google.com/spreadsheets/d/1BabID1n6fPgeuuO4-1r3mkoQ9Nx5dquNwdsET75In1E/edit#gid=790260459&quot;&gt;Google Sheet&lt;/a&gt;&lt;/strong&gt; that collects student extension requests and tracks all extension requests in a master roster.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;Google Cloud Function&lt;/strong&gt; that contains core business logic that:
    &lt;ul&gt;
      &lt;li&gt;Receives form data through a simple &lt;strong&gt;Google Apps Script&lt;/strong&gt; trigger.&lt;/li&gt;
      &lt;li&gt;Process form data in combination with a student’s “record” (which includes DSP status and prior extension requests) to enter either an auto-approval or manual-approval flow.&lt;/li&gt;
      &lt;li&gt;Sends updates to staff through a &lt;strong&gt;Slack Webhook&lt;/strong&gt;, enabling simple internal discussion of student cases through Slack threads.&lt;/li&gt;
      &lt;li&gt;Sends updates to students through the &lt;strong&gt;CS 162 Mailserver&lt;/strong&gt; via &lt;strong&gt;CS 61A’s RPC Interface&lt;/strong&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt;

&lt;h4 id=&quot;traditional-flow&quot;&gt;Traditional Flow&lt;/h4&gt;

&lt;p&gt;Traditionally, courses deal with two types of extensions –&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;DSP Extensions&lt;/strong&gt;, for students with accommodations for assignment extensions&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Non-DSP Extensions&lt;/strong&gt;, for students facing extenuating or otherwise unforeseen circumstances&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Courses traditionally collect extension requests through Google Forms (e.g. ones provided by course managers, like &lt;a href=&quot;https://docs.google.com/forms/d/e/1FAIpQLSek1NWhh6ERQsMJqbkFhv9JtNUPBQsJ3YSoo9uqsFGJ2Lb2iA/viewform&quot;&gt;this one&lt;/a&gt;) or via email. In order to approve these extensions, however, courses (or course managers) need to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Read the student’s request and categorize it into a DSP or Non-DSP extension.&lt;/li&gt;
  &lt;li&gt;Look up whether the student has previously requested assignment extensions.&lt;/li&gt;
  &lt;li&gt;Either (a) update the student’s requested extensions in a central spreadsheet, or (b) update the student’s requested extension on Gradescope/OKPY/PrairieLearn.&lt;/li&gt;
  &lt;li&gt;Send an email to the student containing an “Approved” message, with a new due date.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;challenges&quot;&gt;Challenges&lt;/h4&gt;

&lt;p&gt;The traditional flow results in several challenges, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;High potential for human error.&lt;/strong&gt; In every manual step, there’s a chance for data entry errors that are capable of propagating downstream; in CS 161/CS 61C, we saw a large number of these that arose at the end of the semester when generating final grade reports.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Communication difficulties.&lt;/strong&gt; For classes that outsource work to course managers, there are three parties with different views on extension data: what course managers see, what course staff see, and what students see. All communication, by default, needs to be inclusive of all three parties; if even one email is two-way instead of three-way, then information is “lost”.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Delayed processing times.&lt;/strong&gt; Because of the number of manual steps required here, it can take several days for students to hear back on whether or not their requests were granted, leaving them in a state of stress and uncertainty.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;High barriers to requesting extensions.&lt;/strong&gt; Because there are so many steps in approving each extension, there’s a tendency to write strongly-worded policies discouraging most student extension requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CS 161 Extension Pipeline addresses all of these challenges, significantly &lt;strong&gt;reducing course staff workload&lt;/strong&gt; while simultaneously &lt;strong&gt;improving quality-of-life for students&lt;/strong&gt;.&lt;/p&gt;

&lt;h1 id=&quot;our-pipeline-student-workflow&quot;&gt;Our Pipeline: Student Workflow&lt;/h1&gt;

&lt;p&gt;Students request an extension through a Google Form (see an example &lt;a href=&quot;https://docs.google.com/forms/d/e/1FAIpQLScearqptcOVkcmneQ97zJ07i-r_dtokwhb2rAtTv_WK6a2nJw/viewform&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a student knows which assignments they want to request an extension on,&lt;/strong&gt; then they’re prompted to select from a list of assignments, and provide a number of days for each extension. They can either enter a single number (which will apply to all assignments that they select), or enter comma-separated numbers (to allow them to request a different number of days for different assignments).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127093941023.png&quot; alt=&quot;image-20220127093941023&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a student’s working with a partner&lt;/strong&gt;, then they’re asked to enter their partner’s email and SID. Their  partner will be included in extensions for any assignments that they select which are marked as partner projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a student doesn’t know what assignment they need an extension on,&lt;/strong&gt; they can request a meeting with a TA. We’ve seen this happen for students who’re in extenuating circumstances, and just need to talk through their situation before deciding upon a specific request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a student’s a DSP student with an accommodation for assignment extensions,&lt;/strong&gt; they can declare that on the form. (We recommend that all students who fall under this category receive auto-approvals for extension requests fewer than 7 days.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When a student’s request has been approved (either manually or automatically),&lt;/strong&gt; students receive a templated email with their updated assignment deadlines. &lt;img src=&quot;README.assets/image-20220127094604714.png&quot; alt=&quot;image-20220127094604714&quot; /&gt;&lt;/p&gt;

&lt;p&gt;…and that’s it for students!&lt;/p&gt;

&lt;h1 id=&quot;our-pipeline-staff-workflow&quot;&gt;Our Pipeline: Staff Workflow&lt;/h1&gt;

&lt;p&gt;Staff view all extensions on a master spreadsheet, with two main tabs: a &lt;strong&gt;Form Responses&lt;/strong&gt; tab, which contains all form responses from students, and a &lt;strong&gt;Roster&lt;/strong&gt; tab, which contains a list of all students in the course, with a column for each assignment. The &lt;strong&gt;Roster&lt;/strong&gt; looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127110217827.png&quot; alt=&quot;image-20220127110217827&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When an extension request comes in, staff first receive a Slack message in a private Slack channel.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;If an extension request falls into an auto-approval category,&lt;/u&gt;&lt;/strong&gt; the message contains a summary of the student’s request, as well as a list all of their granted extensions.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127095857467.png&quot; alt=&quot;image-20220127095857467&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When an extension is automatically approved, staff don’t need to do anything!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;If an extension request requires manual approval&lt;/u&gt;&lt;/strong&gt;, the message contains a reason why the request couldn’t be auto-approved.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127103436782.png&quot; alt=&quot;image-20220127103436782&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When an extension requires manual approval, staff should read the reason for the extension, and discuss the request (if needed) in the Slack thread, escalating the case to other staff (e.g. instructors) if necessary.&lt;/p&gt;

&lt;p&gt;If the extension warrants an approval, staff should:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set the &lt;strong&gt;approval_status&lt;/strong&gt; column on the &lt;strong&gt;Roster&lt;/strong&gt; to &lt;strong&gt;“Manually Approved”.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Set the &lt;strong&gt;email_status&lt;/strong&gt; column on the &lt;strong&gt;Roster&lt;/strong&gt; to &lt;strong&gt;“In Queue”&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Use the &lt;strong&gt;Actions =&amp;gt; Dispatch Emails&lt;/strong&gt; menu item to send emails to all students in the queue. This will send emails out to the students in the queue, removing them from the queue as emails are sent, and send an update to Slack when all emails have been sent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127110457234.png&quot; alt=&quot;image-20220127110457234&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If the extension does not warrant an approval (or staff need more information), staff should:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Follow up with the student over email.&lt;/li&gt;
  &lt;li&gt;Clear the &lt;strong&gt;approval_status&lt;/strong&gt; column on the &lt;strong&gt;Roster&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Set the &lt;strong&gt;email_status&lt;/strong&gt; column on the &lt;strong&gt;Roster&lt;/strong&gt; to &lt;strong&gt;Manually Sent&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;If an extension request contains malformed data or any other error occurred,&lt;/u&gt;&lt;/strong&gt; the message contains a description of the error, along with the entire form response.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127100144172.png&quot; alt=&quot;image-20220127100144172&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When an error occurs, staff should:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Correct the error on the &lt;strong&gt;Form Responses&lt;/strong&gt; sheet (or, if it was an internal error due to other data misconfiguration - e.g. missing assignments - correct the configuration).&lt;/li&gt;
  &lt;li&gt;Select the &lt;strong&gt;Reprocess&lt;/strong&gt; checkbox next to any form responses that weren’t fully processed.&lt;/li&gt;
  &lt;li&gt;Use the &lt;strong&gt;Actions =&amp;gt; Reprocess Form Submissions&lt;/strong&gt; menu item to reprocess the failed submissions.&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;If a student requests a student meeting,&lt;/u&gt;&lt;/strong&gt; the message contains a description of the student’s request.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127110739789.png&quot; alt=&quot;image-20220127110739789&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If, during a student meeting (or through some other channel, like a Piazza post), staff would like to grant a student an extension on an assignment, staff should enter the number of days to extend the assignment by directly onto the student record on the &lt;strong&gt;Roster&lt;/strong&gt;, and throw the student record in the queue for outbound emails. This is a natural “form bypass” case, where a form submission isn’t required to grant a student an extension, but these specially-granted extensions are still tracked alongside the rest of the student’s extension requests.&lt;/p&gt;

&lt;h1 id=&quot;configuration&quot;&gt;Configuration&lt;/h1&gt;

&lt;p&gt;All configuration values (e.g. API keys, email options) are stored on the spreadsheet itself, in the &lt;strong&gt;“Environment Variable”&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127111221248.png&quot; alt=&quot;image-20220127111221248&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Assignments may be dynamically configured as well, through the &lt;strong&gt;“Assignments”&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;README.assets/image-20220127111424320.png&quot; alt=&quot;image-20220127111424320&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;edge-cases--faqs&quot;&gt;Edge Cases &amp;amp; FAQ’s&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;In what cases are extensions flagged for human approval?&lt;/u&gt;&lt;/strong&gt;
These cases are flagged for human approval:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The student requests a large number of extension days for any single assignment. This threshold is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTO_APPROVE_THRESHOLD&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTO_APPROVE_THRESHOLD_DSP&lt;/code&gt; for students with DSP accommodations for assignment extensions.&lt;/li&gt;
  &lt;li&gt;The student requests extensions for a large number of assignments. This threshold is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTO_APPROVE_ASSIGNMENT_THRESHOLD&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The &lt;strong&gt;student record&lt;/strong&gt; has “work-in-progress” (e.g. the row on the roster is red or yellow - the student either has an existing pending request or ongoing student meeting).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All other cases are auto-approved! &lt;a href=&quot;https://github.com/cs161-staff/extensions/blob/master/src/handle_form_submit.py#L63&quot;&gt;See here for the logic that handles these cases.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;How do I make it so that all extensions (regardless of status) require manual approval?&lt;/u&gt;&lt;/strong&gt;
If you want tighter control over what’s approved, set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTO_APPROVE_THRESHOLD&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTO_APPROVE_THRESHOLD_DSP&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;. It doesn’t matter what you set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTO_APPROVE_ASSIGNMENT_THRESHOLD&lt;/code&gt; to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;What if a student submits an extension request with a partner?&lt;/u&gt;&lt;/strong&gt;
Any requested extensions for assignments that are “partner” assignments will apply to the designated partner as well as the student. Both student records will be updated on the &lt;strong&gt;Roster&lt;/strong&gt;, and the logic for approval will apply to both partners (e.g. if Partner A submits the form and Partner B has a “work-in-progress” record, then the extension as a whole will be flagged for manual approval).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;What happens if this thing internally combusts in the middle of the semester?&lt;/u&gt;&lt;/strong&gt;
While unlikely, this is a very simple failover case: just process form submissions into the &lt;strong&gt;Roster&lt;/strong&gt; spreadsheet manually, and send templated emails through something like YAMM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Wait, I still have to update the student's due date in OKPY/Gradescope/PL so that they'll be able to turn in their assignments late, right?&lt;/u&gt;&lt;/strong&gt;
You could do this (manually) after each extension request, if you’d like. Alternatively, you could set the “late” due date on these assignments to the end of the semester, and use the extension data on the &lt;strong&gt;Roster&lt;/strong&gt; during grade compilation (this is what CS 161 &amp;amp; CS 61C do). Students will see their assignments marked as “late”, but they’ll be able to use the email they received as proof of their granted extension, just in case they notice inconsistencies in their grade reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;What about long-term maintenance?&lt;/u&gt;&lt;/strong&gt;
Due to the simplicity of this project’s architecture (no frontend, configuration is entirely dynamic, etc.), we don’t anticipate this project needing a lot of long-term maintenance! And feature requests are simple to add, since the code is well-documented with Python class abstractions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;What if I want to change an assignment deadline after extension requests have already come in?&lt;/u&gt;&lt;/strong&gt;
You can handle this in any way that you’d like! Two common approaches –&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Clear out previously-assigned extension requests, and email all students who had requested an extension to re-submit an extension if they need more days past the extended deadline. To clear out existing requests, just clear the assignment column on the &lt;strong&gt;Roster.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Apply all current extension requests to the new deadline. For this, update the deadline in the &lt;strong&gt;Assignments&lt;/strong&gt; tab, filter the &lt;strong&gt;Roster&lt;/strong&gt; to all students with an extension request for the assignment, and add all rows to the email queue; then, dispatch emails. All students who requested an extension will receive a new email with an extended deadline that’s adjusted to the new deadline.&lt;/li&gt;
&lt;/ol&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="projects" /><summary type="html">This post is cross-posted from the README of this project’s GitHub repository. It’s a work in progress, so I’d refer to the README for the latest documentation on how this works!</summary></entry><entry><title type="html">How the Internet Works</title><link href="https://shomil.me/how-the-internet-works/" rel="alternate" type="text/html" title="How the Internet Works" /><published>2021-12-30T12:00:00+00:00</published><updated>2021-12-30T12:00:00+00:00</updated><id>https://shomil.me/how-the-internet-works</id><content type="html" xml:base="https://shomil.me/how-the-internet-works/">&lt;iframe src=&quot;https://docs.google.com/presentation/d/e/2PACX-1vS9m6qcK5bQe2pnLGlW6pWFhATldOHCPAecTQZ7cZQCMDree1HtAwlMsILIAjFUXXd7oPm-zgx7DpMc/embed?start=false&amp;amp;loop=false&amp;amp;delayms=60000&quot; frameborder=&quot;0&quot; width=&quot;100%&quot; height=&quot;400&quot; allowfullscreen=&quot;true&quot; mozallowfullscreen=&quot;true&quot; webkitallowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;View &lt;a href=&quot;https://docs.google.com/presentation/d/e/2PACX-1vS9m6qcK5bQe2pnLGlW6pWFhATldOHCPAecTQZ7cZQCMDree1HtAwlMsILIAjFUXXd7oPm-zgx7DpMc/embed?start=false&amp;amp;loop=false&amp;amp;delayms=60000&amp;amp;slide=id.p&quot;&gt;externally&lt;/a&gt;.&lt;/p&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="teaching" /><summary type="html"></summary></entry><entry><title type="html">Creating an Inclusive Course Environment</title><link href="https://shomil.me/creating-an-inclusive-course/" rel="alternate" type="text/html" title="Creating an Inclusive Course Environment" /><published>2021-12-25T00:00:00+00:00</published><updated>2021-12-25T00:00:00+00:00</updated><id>https://shomil.me/creating-an-inclusive-course</id><content type="html" xml:base="https://shomil.me/creating-an-inclusive-course/">&lt;blockquote&gt;
  &lt;p&gt;This post contains tips on how to create an inclusive course environment. Much of this perspective on pedagogy has been sourced from conversations with some of CS 161’s veteran TAs - Peyrin Kao, Vron Vance, Nicholas Ngai, Fuzail Shakir, and others - as well as from the professors who’ve taught CS 161 over the past few semesters: Nicholas Weaver, David Wagner, and Raluca Ada Popa.&lt;/p&gt;

  &lt;p&gt;This is a work-in-progress. If you have comments, shoot me an email at &lt;a href=&quot;mailto:shomil@berkeley.edu&quot;&gt;shomil@berkeley.edu&lt;/a&gt; or ping me via Slack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;feedback&quot;&gt;Feedback&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ask for feedback, and ask for feedback often.&lt;/strong&gt; I’m starting with, arguably, the most important tip - even if you ignore &lt;strong&gt;everything&lt;/strong&gt; else in this post, please, please do this. Many of the tips I’ve noted here are based on thousands of data points collected through feedback that we’ve sourced directly from students.&lt;/p&gt;

&lt;p&gt;In CS 161, we ask for feedback in two critical places: at the end of each homework assignment in a free-form text box, and immediately after an exam takes place. Asking for this free-form feedback allows us to interface directly with students &amp;amp; resolve concerns as they arise.&lt;/p&gt;

&lt;p&gt;Here’s the question we use to ask for feedback, and an example feedback submission we received from one of our students:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;attachments/feedback.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have a script that scrapes this feedback from Gradescope and dumps it into a Google Doc, which we then review at our weekly staff meetings.&lt;/p&gt;

&lt;p&gt;Occasionally, concerns raised through feedback may be significant enough to warrant a standalone “response” post. Here’s one that David Wagner posted in the Spring 2020 iteration of CS 161, back when I was a student:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;attachments/wagner.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These feedback channels are also a critical component to making students feel heard &amp;amp; building a community in which people feel included, aren’t afraid to ask questions, and share their frustrations constructively (as opposed to &lt;a href=&quot;https://www.reddit.com/r/berkeley/comments/rbe23e/data_100_final_project_is_straight_bullshit/&quot;&gt;destructively&lt;/a&gt;). Here’s an example of feedback (and response) from one of our &lt;strong&gt;“How did the midterm go?”&lt;/strong&gt; posts earlier this semester.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;attachments/exam-feedback.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Aside: for the particular issue above, we course-corrected for the final by dedicating more time/care towards exam pre-testing and having more TA’s in the clarifications room.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;content&quot;&gt;Content&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Deliver content through multiple forms, if possible.&lt;/strong&gt; Over the past few semesters, we released a course textbook to supplement and enhance lecture. We did this to support students who better understand content through text (as opposed to verbal/graphical content delivered through lecture &amp;amp; lecture slides). This semester, I also put together a &lt;a href=&quot;https://docs.google.com/presentation/d/1TFIg7BBLqknzRl59IPGbDlvv53JRsoOZOlw17wilFLY/edit#slide=id.p&quot;&gt;Networking 101&lt;/a&gt; slide deck that covers the foundations of networking in more depth than our traditional lecture slides - this was also well received by students.&lt;/p&gt;

&lt;p&gt;Not everyone learns the same way – so if you’re able to provide several different methods of digesting course content, then do so.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;attachments/textbook.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cs170.org&quot;&gt;CS 170&lt;/a&gt; does this really, really well (they offer lecture, reading, section/section walkthrough, mini-lecture/conceptual walkthroughs, and assignments/assignment walkthroughs).&lt;/p&gt;

&lt;p&gt;Also, collect data on what works for students - and put TA hours toward building out the things that work. We polled students on how helpful they found the textbook in SU21 and FA21 (while we were actively developing it), and we saw an increase of nearly 20% of students who rated the helpfulness of the textbook a 4/4.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img title=&quot;&quot; src=&quot;attachments/notes-useful.png&quot; alt=&quot;&quot; width=&quot;517&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;People generally like video walkthroughs of things.&lt;/strong&gt; Our video walkthrough for &lt;a href=&quot;https://www.youtube.com/watch?v=psxuAU9PycE&quot;&gt;Project 1&lt;/a&gt; (1300 views) and &lt;a href=&quot;https://www.youtube.com/watch?v=HVw4z_RVH20&quot;&gt;Project 2&lt;/a&gt; (900 views) were among our most-viewed videos for the entire semester. We have a hunch that these were particularly helpful to students who were conceptually behind and/or felt lost – considering we had only 600 enrolled students, it seems many students referenced these videos several times.&lt;/p&gt;

&lt;h2 id=&quot;logistics&quot;&gt;Logistics&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Be lenient with extensions.&lt;/strong&gt; Life as a student (especially at UC Berkeley) is often fatiguing, chaotic, and incredibly stressful. As instructors, many of the things we do directly and indirectly affect our students’ mental health. If we’re given opportunities to reduce student stress (e.g. through a one or two-day extension, a few added slip days due to power outages, etc.) – we should do everything in our power to make it happen.&lt;/p&gt;

&lt;p&gt;One strategy: when planning your course schedule at the beginning of the semester, throw in a scattering of buffer days – and as the semester progresses, sprinkle a few random (intentional) extensions in as your “surprise” stress-relievers. &lt;a href=&quot;https://www.reddit.com/r/berkeley/comments/rdqh4j/drama_in_ee_120_who_do_you_side_with/?utm_source=share&amp;amp;utm_medium=ios_app&amp;amp;utm_name=iossmf&quot;&gt;And on that note, here’s an example of what not to do.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be wary of what and when you’re email-blasting.&lt;/strong&gt; Be cognizant of what times you’re email-blasting information – especially sensitive/important information. Blasting exam grades at 9:30 PM on a Friday might not be a move…perhaps wait until Saturday morning for an email blast. Here are a few other tips related to email blasts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Ask for ACKs from at least one other TA before email-blasting something.&lt;/li&gt;
  &lt;li&gt;Send out a consistent weekly announcement that contains assignments &amp;amp; due dates for the week.&lt;/li&gt;
  &lt;li&gt;Try to keep email blasts to 1-2 a week.&lt;/li&gt;
  &lt;li&gt;On that note, use targeted emails instead of email-blasting when possible (e.g. if a discussion attended by 30/1000 is canceled, it’s probably not worth pinging all 1000 students).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Kind words are important.&lt;/strong&gt; In 161, we ask our proctors to run down a list of announcements at the start of an exam – and the very last announcement is always a set of kind words (e.g. “You’ll all do great!”). Similarly, we try to begin and end our email blasts with a few kind words as well, rather than going straight to business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try to build in support for one-on-one meetings with students.&lt;/strong&gt; We have a few TA’s who dedicate a few of their hours towards these meetings each week - and they say every one of these meetings ends with the student saying something like “this has been so helpful, I was really stressed out before the meeting and now I am in such a good place and ready for…”. The difference between a private Piazza post or email and directly speaking to a struggling student is astounding. Discussions with &amp;amp; observations of these meetings in larger courses (e.g 61B, 61C) suggest that the effectiveness of these meetings require complementary extension-related course policy to scale as well (related: &lt;a href=&quot;https://docs.google.com/document/d/1tJa-e48socf9LDCtQn0fzGqnTOn69qRhs7HUZG3IQsk/edit#heading=h.bnp8gflkqbmu&quot;&gt;see these tips&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;piazza&quot;&gt;Piazza&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Give people updates on what’s happening on Piazza, instead of leaving posts unresolved for a long period of time.&lt;/strong&gt; Try to make sure every single post &amp;amp; followup has a response. One thing that we use that I’d personally love to see in other classes: if you’re blocked on immediately resolving a post or don’t know what the answer is, then respond with a quick “Investigating” or “I’ve pinged XYZ to look into this” so students don’t think you’re ignoring posts. I think this might be especially relevant for megathreads &amp;amp; debugging-related posts that may go unresolved for a longer amount of time.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;attachments/piazza-updates.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Instead of flat-out rejecting student demands, compromise wherever possible.&lt;/strong&gt; Here’s an example: we release grade reports three times throughout the semester - after the midterm, before the final, and after the final. In our first round of grade reports, we received feedback (through our HW feedback channel) that percentiles were causing a stressful, competitive environment, where people were forced to compare themselves to their peers when looking at their grade reports. As such, we took these percentiles out in the second round. This immediately received pushback on Piazza – to which, we re-discussed and released this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;attachments/compromise.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Be transparent.&lt;/strong&gt; Earlier this semester, we released a buggy autograder, with some students receiving lower-than-intended scores. When we re-released grades a few days later, we posted this note along with our update.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;attachments/transparent.png&quot; alt=&quot;drawing&quot; width=&quot;100%&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;website&quot;&gt;Website&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Have a centralized course website as a source of truth.&lt;/strong&gt; Try to avoid scattering information across Piazza, Google Docs, emails, and a website - just put everything onto the website, and attach links to the websites from other sources. A clean web frontend is much, much better than a Piazza post or a Google Doc - and it’s certainly easier to find.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put all important course links on your site’s homepage.&lt;/strong&gt; This includes things like a standardized form to request extensions (here’s an &lt;a href=&quot;https://docs.google.com/forms/d/e/1FAIpQLSfgtF7Wti0l0mi08Qze0HQsHbqhrqT2jw_G7hptw1J9ANCfsg/viewform&quot;&gt;example&lt;/a&gt;) and the office hours queue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please don’t conflate cheating with collaboration in your course syllabus.&lt;/strong&gt; An earlier iteration of the &lt;a href=&quot;https://inst.eecs.berkeley.edu/~cs61c/fa20/policies&quot;&gt;CS 61C syllabus&lt;/a&gt; contained this note:  &lt;em&gt;We don’t want you sharing approaches, ideas, code or whiteboarding with other students.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At the time, it blew my mind that an introductory-level CS class at a top-tier university had an outright ban on whiteboarding with other students. Of course, this was in the context of academic integrity around course projects - but the specific wording that this particular policy used seemed (from my perspective as a student) cut-throat, competitive, and anti-collaborative.&lt;/p&gt;

&lt;p&gt;I thought CS 184’s “Learning Cooperatively” section from the Spring 2021 syllabus was worded quite nicely: &lt;em&gt;With the obvious exception of exams, we encourage you to discuss course activities with your friends and classmates as you are working on them. You will definitely learn more in this class if you work with others than if you do not. Ask questions, answer questions, and share ideas liberally.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And…again, the context here is a little different (e.g. upper-division vs. lower-division course), but my point here stands: &lt;strong&gt;emphasize collaboration in your syllabus, rather than denouncing it under the guise of “academic integrity”.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;exams&quot;&gt;Exams&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Always, always, always simplify student-facing logistics.&lt;/strong&gt; When designing policies, procedures, and other student-facing documents, keep things simple - as simple as possible. The most critical example of this: exam logistics &lt;strong&gt;should never exceed one page.&lt;/strong&gt; Here’s an example of &lt;a href=&quot;https://cs161.org/exam&quot;&gt;what to do&lt;/a&gt;, and &lt;a href=&quot;https://docs.google.com/document/d/1TQojO7tDzQtGzuMAFiXIBwvEEmX_Z454b1yzvcrqzzc/edit&quot;&gt;what not to do&lt;/a&gt;. Both of these examples are from Fall 2021.&lt;/p&gt;

&lt;p&gt;During the early days of Zoom proctoring, proctoring policies sometimes exceeded ~12-17 pages (yes, this &lt;a href=&quot;https://www.reddit.com/r/berkeley/comments/j1rznd/excessive_zoom_proctoring_shouldnt_be_allowed/&quot;&gt;really happened&lt;/a&gt;). Creating excessive hoops for students to jump through distracts from actual course content – not to mention that we can achieve the same degree of effectiveness through a one-page policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the exam-taking experience as smooth as possible.&lt;/strong&gt; Here are a few easy-to-catch things that we look for in our exams to make the experience better for our students:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you’re offering hybrid exams, pay close attention to equity across formats (e.g. limit short-answer questions to one sentence, so you’re not giving fast typers a significant advantage).&lt;/li&gt;
  &lt;li&gt;When pre-testing, &lt;em&gt;print out the exam&lt;/em&gt;. Sometimes, how pages line up influence the exam taking process (e.g. question on the front of a page, with the answer box on the back).&lt;/li&gt;
  &lt;li&gt;Try to stay away from using big, convoluted blocks of text. Keep sentences short &amp;amp; concise to ensure the focus remains on course material.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exams shouldn’t leave students feeling depressed.&lt;/strong&gt; Follow Dan Garcia’s &lt;a href=&quot;https://eecs.berkeley.edu/resources/gsis/current/prep&quot;&gt;tips on writing good exam questions&lt;/a&gt;. A few major recommendations –&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Strive to make questions have a balance of A-level, B-level, and C-level questions.&lt;/li&gt;
  &lt;li&gt;Every student should get some credit on each problem.&lt;/li&gt;
  &lt;li&gt;Most problems should have a “fun” part that makes students think.&lt;/li&gt;
  &lt;li&gt;Try very, very hard to keep exam means around 50-70%. Opening Gradescope to see a 30/100 is something that &lt;strong&gt;does&lt;/strong&gt; impact a student’s mental health. I’ve been there - it really sucks to be in a position where you’re being told that you only “understood” 30% of a course, even if the overall class mean is low.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Allow students to discuss/ask questions about exam rubrics on Piazza, instead of taking a stance of “course staff knows best”.&lt;/strong&gt; Here’s the message we post on Piazza: &lt;em&gt;As always, it’s certainly possible that we may have made mistakes in grading. Please submit a regrade request if you spot an issue with how we applied the rubric to your submission, and feel free to leave questions (and any other exam-related feedback) in the follow-ups to this thread.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When discussing regrades, student-facing tone matters - a lot.&lt;/strong&gt; Use neutral/positive phrases like “definitely open to hearing second opinions about this”, or “I don’t think we can give points for [X] here”, instead of negative/combative phrases. Regrade requests shouldn’t be an “us-vs-them” battle to the death; listen to students, try to hear their perspective, and share your own perspective until you’re able to reach a satisfactory conclusion.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;attachments/regrades.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Add a course bot!&lt;/strong&gt; A few of our TA’s have been developing EvanBot (along with a pretty integrated course storyline) over the course of the past few semesters. Here are a few of Bot’s capabilities: &lt;a href=&quot;attachments/bot-1.png&quot;&gt;making sensitive remarks about grading&lt;/a&gt;, &lt;a href=&quot;attachments/bot-2.png&quot;&gt;responding to logistics requests in humorous ways&lt;/a&gt;, and &lt;a href=&quot;attachments/bot-3.png&quot;&gt;sliding in a sly comment or two.&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;attachments/bot-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;internal&quot;&gt;Internal&lt;/h2&gt;

&lt;p&gt;If you’re a head TA, these are a few things that have worked well on 161 staff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Emphasize transparency.&lt;/strong&gt; We’ve seen significant value from discussing admin-level logistics with our entire staff (e.g. much of our proctoring policy was crafted in a staff meeting a few semesters ago, and our 8-hour TA’s frequently contribute to decisions regarding course policy/workload and exam design). Creating barriers &amp;amp; hierarchy among staff degrades staff cohesion, reduces operational efficiency, and just doesn’t work well. We’re not &lt;a href=&quot;https://www.vox.com/2017/9/11/16288896/apple-secrecy-inkling-culture-leadership-transparency-values&quot;&gt;Apple&lt;/a&gt; - we’re a bunch of (under)graduate student instructors just trying to teach our peers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep roles flexible.&lt;/strong&gt; If someone turns out to be really good at something, then let them work on that - even if it isn’t their official role. And, of course, reallocate other staff hours towards reducing their pre-assigned burden, if they’ve found something that they’re able to do really really well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep hours flexible for office-hours “on-call” requests.&lt;/strong&gt; This is super important during project weeks - if the queue is long, whoever’s assigned to a particular slot should ping Slack with a @here requesting assistance. Set an expectation for TA’s to dedicate a few staff hours towards being “on-call” during crunch weeks, and doubling up or stacking busy office hour times.&lt;/p&gt;

&lt;h2 id=&quot;a-few-additional-resources&quot;&gt;A Few Additional Resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/18NBHwRaClsXc17mdzVd2x-kZkFldyuLlnd01lfUQxrg/edit&quot;&gt;Proposed set of unified DSP policies&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/1tJa-e48socf9LDCtQn0fzGqnTOn69qRhs7HUZG3IQsk/edit#heading=h.bnp8gflkqbmu&quot;&gt;Sruthi’s recommendations for one-on-one meetings&lt;/a&gt; [from CS 61C]&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/1gFYRI0_uQ8JJJ5U5C-wrrGFm1Z5SSwMzBvORFqAoiX8/edit?usp=sharing&quot;&gt;Head TA’s guide to CS 161&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="articles" /><summary type="html">This post contains tips on how to create an inclusive course environment. Much of this perspective on pedagogy has been sourced from conversations with some of CS 161’s veteran TAs - Peyrin Kao, Vron Vance, Nicholas Ngai, Fuzail Shakir, and others - as well as from the professors who’ve taught CS 161 over the past few semesters: Nicholas Weaver, David Wagner, and Raluca Ada Popa. This is a work-in-progress. If you have comments, shoot me an email at shomil@berkeley.edu or ping me via Slack.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://shomil.me/assets/img/creating-an-inclusive-course.png" /></entry><entry><title type="html">Reverse Engineering 101 - How to Hack Snackpass</title><link href="https://shomil.me/how-to-hack-snackpass/" rel="alternate" type="text/html" title="Reverse Engineering 101 - How to Hack Snackpass" /><published>2021-11-02T00:00:00+00:00</published><updated>2021-11-02T00:00:00+00:00</updated><id>https://shomil.me/how-to-hack-snackpass</id><content type="html" xml:base="https://shomil.me/how-to-hack-snackpass/">&lt;p&gt;Here’s a pretty neat presentation I gave on reverse engineering!&lt;/p&gt;

&lt;style&gt;
.responsive-wrap iframe{ max-width: 100%;}
&lt;/style&gt;

&lt;div class=&quot;responsive-wrap&quot;&gt;
&lt;!-- this is the embed code provided by Google --&gt;
&lt;iframe src=&quot;https://docs.google.com/presentation/d/15esHJYPi_hxE2Tbc5w_aGuezOdB4c0bHV4b_oRI3EEA/embed?start=false&amp;amp;loop=false&amp;amp;delayms=60000&quot; frameborder=&quot;0&quot; width=&quot;960&quot; height=&quot;569&quot; allowfullscreen=&quot;true&quot; mozallowfullscreen=&quot;true&quot; webkitallowfullscreen=&quot;true&quot;&gt;
    &lt;/iframe&gt;
&lt;!-- Google embed ends --&gt;
&lt;/div&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="projects" /><category term="security" /><summary type="html">Here’s a pretty neat presentation I gave on reverse engineering!</summary></entry><entry><title type="html">CS 161 (Computer Security) Fall 2021 - All Resources</title><link href="https://shomil.me/cs161-fa21/" rel="alternate" type="text/html" title="CS 161 (Computer Security) Fall 2021 - All Resources" /><published>2021-08-30T12:00:00+00:00</published><updated>2021-08-30T12:00:00+00:00</updated><id>https://shomil.me/cs161-fa21</id><content type="html" xml:base="https://shomil.me/cs161-fa21/">&lt;p&gt;Hey! I’ll be using this page to post resources from my 161 Discussion Section for &lt;strong&gt;Fall 2021.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’d appreciate your feedback! If you have any thoughts on my section, please fill out &lt;a href=&quot;https://airtable.com/shr762qgTwjZPiTtE&quot;&gt;this anonymous form&lt;/a&gt; - or, shoot me an email (shomil@).&lt;/p&gt;

&lt;p&gt;If I haven’t posted slides for the most recent discussion yet, feel free to view my slides from previous semesters. For the most part, they should be similar!&lt;/p&gt;

&lt;h3 id=&quot;discussion-resources&quot;&gt;Discussion Resources&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Discussion&lt;/th&gt;
      &lt;th&gt;Resources&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Discussion 1&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1Ty8dq-Pfyppna2Cy_lA86b0xTmeCSboDGfYmZsxNKmE/edit?usp=sharing&quot;&gt;Slides&lt;/a&gt; (x86, GDB, Security Principles)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Discussion 2/3&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/154qbyVvvGulkgnLh2-7A7bkHVB4I-hrAUM-2pr7FN1g/edit?usp=sharing&quot;&gt;Slides&lt;/a&gt; (Memory Safety)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Discussion 4&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/11h7zx9VRY5gKBE1b42Oql10C5vXDrvTk9K074HOEtzg/edit?usp=sharing&quot;&gt;Slides&lt;/a&gt; (Intro to Cryptography)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Discussion 5&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1YBEDlLXE__9ZrwO2QMwONKozvQq7VxJEnsadnQkS9tA/edit?usp=sharing&quot;&gt;Slides&lt;/a&gt; (Crypto II)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Midterm Review&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/10FVBVgK3ZS3To2roqERIW71ORH_UPYqJ4-w-cX9Jhj0/edit?usp=sharing&quot;&gt;Slides&lt;/a&gt; (Memory Safety)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Discussion 7&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1nlLnQVqutK2gGUpbnOA952oRp0J1KFB7jsoA3Rue9nE/edit#slide=id.gf7322f1beb_0_132&quot;&gt;Slides&lt;/a&gt; (SOP, Cookies)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Discussion 8&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1kdRkhUvj9s4HGJkrHtuEb6j0loDPYfm8nNT1_SOQqZQ/edit#slide=id.gfcd1139d27_0_113&quot;&gt;Slides&lt;/a&gt; (SOP, Cookies, CSRF, XSS)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Out of scope, but still highly recommend: &lt;a href=&quot;https://docs.google.com/presentation/d/1MGFct3gFW_HfdWI8Zr1Xw4-Z7f5yZABP25GpgztaAGw/edit#slide=id.p&quot;&gt;Reverse Engineering 101&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Also, here’s my &lt;a href=&quot;https://shomil.me/assets/pdf/161/final.pdf&quot;&gt;Final Review Sheet&lt;/a&gt; from Spring 2020. Disclaimer: I made this when I took this class and haven’t really looked at it since, so there might be a typo or two! It also might not fully encapsulate the scope of this semester’s final.&lt;/strong&gt;&lt;/p&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="teaching" /><summary type="html">Hey! I’ll be using this page to post resources from my 161 Discussion Section for Fall 2021.</summary></entry><entry><title type="html">My CS 184 Course Notes</title><link href="https://shomil.me/cs184-notes/" rel="alternate" type="text/html" title="My CS 184 Course Notes" /><published>2021-05-14T17:00:00+00:00</published><updated>2021-05-14T17:00:00+00:00</updated><id>https://shomil.me/cs184-notes</id><content type="html" xml:base="https://shomil.me/cs184-notes/">&lt;p&gt;I took CS 184 (Computer Graphics) in Spring 2021, and I wrote up my notes from the course &lt;a href=&quot;https://shomil.notion.site/CS-184-Notes-bfb4c4fd3bc842538ec3510c20cc3a60&quot;&gt;here&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;image-20211226163855943.png&quot; alt=&quot;image-20211226163855943&quot; /&gt;&lt;/p&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="projects" /><category term="notes" /><summary type="html">I took CS 184 (Computer Graphics) in Spring 2021, and I wrote up my notes from the course here!</summary></entry><entry><title type="html">EECS 101 - a Repository of Crowdsourced Course Advice</title><link href="https://shomil.me/eecs-101/" rel="alternate" type="text/html" title="EECS 101 - a Repository of Crowdsourced Course Advice" /><published>2020-11-25T05:00:00+00:00</published><updated>2020-11-25T05:00:00+00:00</updated><id>https://shomil.me/eecs-101</id><content type="html" xml:base="https://shomil.me/eecs-101/">&lt;p&gt;&lt;img src=&quot;image-20201126130608439.png&quot; alt=&quot;image-20201126130608439&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As course advising at UC Berkeley has shifted to a predominantly online format, the popularity of the EECS 101 Piazza for course &amp;amp; advising-related inquiries has increased rapidly. Over 4,000 students actively browse the forum, in search of everything from waitlist-related questions to workload discussions and extracurricular opportunities.&lt;/p&gt;

&lt;p&gt;One of the biggest bottlenecks on this forum - something that applies to Piazza in general - is the lack of functional search. Searching for even simple queries yields widely inaccurate results. Piazza seems to use some combination of exact word matching and time-based sorting - and it’s incredibly frustrating for anyone trying to search for answers in a repository of over 13,500 Q&amp;amp;A posts.&lt;/p&gt;

&lt;p&gt;This past week, I decided to do something about it - I launched &lt;a href=&quot;https://eecs101.org/&quot;&gt;https://eecs101.org/&lt;/a&gt;, a site that indexes content from EECS 101 and serves it in a clean format with robust search &amp;amp; filter capabilities. Below, I walk through the process of approaching this.&lt;/p&gt;

&lt;p&gt;View this project’s source code &lt;a href=&quot;https://github.com/shomilj/Berkeleyspace&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;evaluating-search-functionality&quot;&gt;Evaluating Search Functionality&lt;/h1&gt;

&lt;h4 id=&quot;search-query-188-vs-189&quot;&gt;Search Query: “188 vs 189”&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;image-20201126134602696.png&quot; alt=&quot;image-20201126134602696&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;image-20201126134643165.png&quot; alt=&quot;image-20201126134643165&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;search-query-170-workload&quot;&gt;Search Query: “170 Workload”&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;image-20201126134720890.png&quot; alt=&quot;image-20201126134720890&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;image-20201126134744123.png&quot; alt=&quot;image-20201126134744123&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;search-query-cs-161-waitlist&quot;&gt;Search Query: “CS 161 Waitlist”&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;image-20201126134902184.png&quot; alt=&quot;image-20201126134902184&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;image-20201126134845975.png&quot; alt=&quot;image-20201126134845975&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;extra-how-i-built-it&quot;&gt;Extra: How I Built It&lt;/h1&gt;

&lt;h5 id=&quot;1-acquiring-the-dataset-scraping-piazza&quot;&gt;1. Acquiring the Dataset: Scraping Piazza&lt;/h5&gt;

&lt;p&gt;To scrape Piazza, I used this open-source &lt;a href=&quot;https://github.com/hfaran/piazza-api&quot;&gt;unofficial Piazza API&lt;/a&gt;. I let it run for about an hour and a half with a one-second delay after each API call to avoid rate limits. I saved posts to a local &lt;a href=&quot;https://tinydb.readthedocs.io/en/latest/getting-started.html&quot;&gt;TinyDB&lt;/a&gt; JSON database as a form of temporary storage.&lt;/p&gt;

&lt;p&gt;See the source code &lt;a href=&quot;https://github.com/shomilj/Berkeleyspace-Preprocessing/blob/master/scrape-piazza.ipynb&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h5 id=&quot;2-tokenizing-the-dataset&quot;&gt;2. Tokenizing the Dataset&lt;/h5&gt;

&lt;p&gt;To assist with rapid search functionality, I decided to extract key tokens from each post ahead of time. For the time being, this includes &lt;strong&gt;professors&lt;/strong&gt; and &lt;strong&gt;courses&lt;/strong&gt; - but more metadata tokens could be extracted at this step.&lt;/p&gt;

&lt;p&gt;The logic for this token extraction is simple - it simply uses an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract_tags&lt;/code&gt; function, which looks to find and replace keywords (without replacement) in order from longest length to shortest length.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example: if {CS 189, CS189, 189} are keywords that correspond to token CS189, then we search for CS 189 first, then CS189, then 189 - and if any of those match, we remove them from the search content temporarily and update the post’s tags while scanning for other keywords.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;See the source code &lt;a href=&quot;https://github.com/shomilj/Berkeleyspace-Preprocessing/blob/master/process-piazza.ipynb&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h5 id=&quot;3-building-a-react-frontend&quot;&gt;3. Building a React Frontend&lt;/h5&gt;

&lt;p&gt;To build a quick frontend, I took inspiration from Berkeleytime’s format &amp;amp; style. For formatting, I used four main react components:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;TabBar, to hold the navigation bar.&lt;/li&gt;
  &lt;li&gt;FilterView, to hold the search bar and any filters.&lt;/li&gt;
  &lt;li&gt;ListView, to hold a list of cells with search results.&lt;/li&gt;
  &lt;li&gt;DetailView, to display the post content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing too complex here.&lt;/p&gt;

&lt;h5 id=&quot;4-building-a-nodejsexpress-backend--implementing-search&quot;&gt;4. Building a NodeJS/Express Backend &amp;amp; Implementing Search&lt;/h5&gt;

&lt;p&gt;To build a quick backend, I created two endpoints using NodeJS/Express – a &lt;strong&gt;/query&lt;/strong&gt; endpoint, which queries the my cached Piazza database for a list of posts given search content/filters, and a &lt;strong&gt;/content&lt;/strong&gt; endpoint, which queries the database for actual post content.&lt;/p&gt;

&lt;p&gt;This is where the majority of the search logic was implemented - I added support for a handful of different search types, and optimized particular types (e.g. auto) to support entity extraction on the search query itself.&lt;/p&gt;

&lt;h5 id=&quot;5-connecting-the-frontendbackend-with-redux&quot;&gt;5. Connecting the Frontend/Backend with Redux&lt;/h5&gt;

&lt;p&gt;I used React-Redux to connect my data stream &amp;amp; API calls to React components.&lt;/p&gt;

&lt;h5 id=&quot;6-deploying-the-site-to-heroku&quot;&gt;6. Deploying the Site to Heroku&lt;/h5&gt;

&lt;p&gt;I deployed the site to a Heroku dyno!&lt;/p&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="projects" /><summary type="html"></summary></entry><entry><title type="html">SWE Portfolio - Mobile Apps</title><link href="https://shomil.me/apps/" rel="alternate" type="text/html" title="SWE Portfolio - Mobile Apps" /><published>2020-10-01T00:00:00+00:00</published><updated>2020-10-01T00:00:00+00:00</updated><id>https://shomil.me/apps</id><content type="html" xml:base="https://shomil.me/apps/">&lt;h1 id=&quot;projects&quot;&gt;Projects&lt;/h1&gt;

&lt;h3 id=&quot;orbit-apps-bear-central-lghs-etc&quot;&gt;Orbit Apps (Bear Central, LGHS, etc.)&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;image-20201027155723770.png&quot; alt=&quot;image-20201027155723770&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href=&quot;https://apps.apple.com/us/app/bear-central/id1441903611&quot;&gt;Bear Central (UC Berkeley)&lt;/a&gt;, &lt;a href=&quot;https://apps.apple.com/us/app/lg-cats/id1418860317&quot;&gt;LG CATS (Los Gatos High)&lt;/a&gt;, &lt;a href=&quot;https://apps.apple.com/us/app/orbit-for-organizations/id1449112040&quot;&gt;Orbit (Generic)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Context:&lt;/strong&gt; I started this project in high school and continued working on it through college!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; This is a multi-year project that I worked on! It started as an app for my high school (LG CATS) – but over time, it evolved into a platform used to create apps for schools and organizations in general, with the core component being a &lt;a href=&quot;https://getorbit.app&quot;&gt;no-code engine&lt;/a&gt; that transformed data from Google Sheets into native iOS apps.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Swift/Xcode, using Firebase as a backend and Google Sheets as a content manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;bend-the-curve&quot;&gt;Bend the Curve&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;image-20201027155741740.png&quot; alt=&quot;image-20201027155741740&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href=&quot;https://shomil.me/bend-the-curve/&quot;&gt;Blog Post&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Context:&lt;/strong&gt; I created this over quarantine in May 2020.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; This is a simple, UI-focused app focused on tracking COVID cases across the world.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Swift/Xcode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ai-generated-resumes&quot;&gt;AI-Generated Resumes&lt;/h3&gt;

&lt;video muted=&quot;&quot; controls=&quot;&quot; width=&quot;40%&quot; style=&quot;display:block; margin:0 auto; border-style: dotted; border-width: 1px; border-color: #ebeff0&quot;&gt;
    &lt;source src=&quot;https://shomil.me/resume-generation/recruiter.mov&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href=&quot;https://shomil.me/resume-generation/&quot;&gt;Blog Post&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Context:&lt;/strong&gt; I created this for Cal Hacks 2019.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; This is a simple game! Check out the demo video in the post.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Swift/Xcode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;companies&quot;&gt;Companies&lt;/h1&gt;

&lt;h3 id=&quot;paz-by-pineal-labs&quot;&gt;Paz, by Pineal Labs&lt;/h3&gt;

&lt;video muted=&quot;&quot; controls=&quot;&quot; width=&quot;40%&quot; style=&quot;display:block; margin:0 auto; border-style: dotted; border-width: 1px; border-color: #ebeff0&quot;&gt;
    &lt;source src=&quot;https://shomil.me/paz-app/screen_recording.mov&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href=&quot;http://shomil.me/paz-app/&quot;&gt;Blog Post&lt;/a&gt;, &lt;a href=&quot;https://apps.apple.com/us/app/paz-relax-sleep-meditate/id1500085577&quot;&gt;iOS App Store&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Role:&lt;/strong&gt; I was the first iOS engineer for Pineal Labs, and built this app for the Berkeley-based meditative music startup.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; This app is a music-focused meditation app.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Swift/Xcode, using Firebase as a backend database and Airtable as a content manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;equilibrium-by-vayu-technology&quot;&gt;Equilibrium by Vayu Technology&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;image-20201027155926979.png&quot; alt=&quot;image-20201027155926979&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href=&quot;https://apps.apple.com/us/app/equilibrium-by-vayu-technology/id1264121687&quot;&gt;iOS App Store&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Role:&lt;/strong&gt; I was the first iOS engineer for Vayu Technology, and built this app for the LA-based wearable technology startup.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; This app is used to distribute OTA firmware upgrades to Equilibrium devices.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Swift/Xcode, using iOS’s Core Bluetooth framework to implement communication with the startup’s wearable sensors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;messenger-terabit-networks&quot;&gt;Messenger Terabit Networks&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Links:&lt;/strong&gt; Not Released (Internal App)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Role:&lt;/strong&gt; I was the first iOS engineer for MTN, and built this app for the Los Gatos-based wearable technology startup.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; This app serves as the primary interface to the proprietary MTN electronic logging device for trucks.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Swift/Xcode, using iOS’s Core Bluetooth framework to communicate with the logging device.&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="apps" /><summary type="html">Projects</summary></entry><entry><title type="html">Voiceroom - spatially-aware voice chat</title><link href="https://shomil.me/voiceroom/" rel="alternate" type="text/html" title="Voiceroom - spatially-aware voice chat" /><published>2020-09-01T00:00:00+00:00</published><updated>2020-09-01T00:00:00+00:00</updated><id>https://shomil.me/voiceroom</id><content type="html" xml:base="https://shomil.me/voiceroom/">&lt;blockquote&gt;
  &lt;p&gt;This project is in progress, so this post may be updated with more information &amp;amp; updates over the coming months!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Earlier this summer, a friend and I built Voiceroom (https://voiceroom.us/) – a spatially-aware voice chat application, built using WebRTC and React. We launched on &lt;a href=&quot;https://www.producthunt.com/posts/voiceroom&quot;&gt;Product Hunt&lt;/a&gt; - and made it to the front page! We were also featured in the &lt;a href=&quot;https://www.producthunt.com/newsletter/6231&quot;&gt;Product Hunt Newsletter&lt;/a&gt;, which goes out to thousands of readers daily. Voiceroom was also mentioned in the &lt;a href=&quot;https://www.wsj.com/articles/remember-office-banter-audio-apps-want-to-bring-that-back-11598439617&quot;&gt;Wall Street Journal&lt;/a&gt;, in an article focusing on the rise in audio-first experiences.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;A little more about Voiceroom (quoted from our Product Hunt post) –&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hey Product Hunt!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We’re excited to launch Voiceroom, a service designed to make virtual interactions more realistic.  As students, we’ve spent hours on Zoom and Google Hangouts where only one person can speak at a time. We miss the real-life environment of open collaboration – and the ability to launch into a side conversation with the person next to you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To solve that problem, we built Voiceroom. With Voiceroom, multiple simultaneous conversations can happen in one room, and you’ll be able to hear both the immediate conversation around you as well as other conversations in the background. It’s like a real room. If you’re presenting, you can screen share and broadcast your voice so that the whole room can hear you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Voiceroom currently supports all major browsers (Chrome, Safari, and Firefox) and rooms up to 15 people. We’re working hard on bringing support to mobile – as well as expanding the number of participants in each room.  We’re continuously improving the experience, and would love to hear your feedback!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Anmol &amp;amp; Shomil&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update: try the demo at &lt;a href=&quot;https://www.voiceroom.us/demo&quot;&gt;https://www.voiceroom.us/demo&lt;/a&gt; to see this in action without making an actual room!&lt;/em&gt;&lt;/p&gt;</content><author><name>Shomil Jain</name><email>shomil@berkeley.edu</email></author><category term="projects" /><summary type="html">This project is in progress, so this post may be updated with more information &amp;amp; updates over the coming months!</summary></entry></feed>