<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Spherical Flying Kat</title>
    <description>The latest articles on DEV Community by Spherical Flying Kat (@sphericalkat).</description>
    <link>https://dev.to/sphericalkat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F362986%2F778574b7-a2d0-4bdb-8018-a2cee93088f9.jpeg</url>
      <title>DEV Community: Spherical Flying Kat</title>
      <link>https://dev.to/sphericalkat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sphericalkat"/>
    <language>en</language>
    <item>
      <title>WebRTC 102: #3 Getting Real with RTP and RTCP</title>
      <dc:creator>Spherical Flying Kat</dc:creator>
      <pubDate>Mon, 20 Mar 2023 13:56:11 +0000</pubDate>
      <link>https://dev.to/dyte/webrtc-102-3-getting-real-with-rtp-and-rtcp-jep</link>
      <guid>https://dev.to/dyte/webrtc-102-3-getting-real-with-rtp-and-rtcp-jep</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Real-time communication over the internet has become increasingly popular in recent years, and WebRTC has emerged as one of the leading technologies for enabling it over the web. WebRTC uses a variety of protocols, including Real-Time Transport Protocol (RTP) and Real-Time Control Protocol (RTCP).&lt;/p&gt;

&lt;p&gt;RTP is responsible for transmitting audio and video data over the network, while RTCP is responsible for monitoring network conditions and providing feedback to the sender. RTP and RTCP communicate over the same network, with RTP using even-numbered ports and RTCP using odd-numbered ports. This allows both protocols to use the same network resources without interfering with each other.&lt;/p&gt;

&lt;p&gt;In this post, we will discuss what RTP and RTCP are and how they work together to enable real-time communication in WebRTC.&lt;/p&gt;

&lt;p&gt;This is the third part of our ongoing WebRTC 102 blog series — in the second one we covered &lt;a href="https://dyte.io/blog/understanding-libwebrtc/"&gt;libWebRTC&lt;/a&gt; and in the first one, &lt;a href="https://dyte.io/blog/webrtc-102-demystifying-ice/"&gt;we tackled ICE&lt;/a&gt; and understood how those work under the hood.&lt;/p&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Transport Protocol (RTP)
&lt;/h3&gt;

&lt;p&gt;Real-Time Transport Protocol (RTP) is a protocol designed for transmitting audio and video data over the internet. RTP is used to transport media streams, such as voice and video, in real-time.&lt;/p&gt;

&lt;p&gt;RTP is responsible for packetizing media data into small packets and transmitting it over the network. Each RTP packet contains a sequence number and timestamp, which are used to ensure that the packets are delivered in the correct order and at the correct time. RTP packets are transmitted over UDP, which provides low latency and is ideal for real-time communication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Control Protocol (RTCP)
&lt;/h3&gt;

&lt;p&gt;Real-Time Control Protocol (RTCP) is a protocol designed to provide feedback on the quality of service (QoS) of RTP traffic. RTCP is used to monitor network conditions, such as packet loss and delay, and to provide feedback to the sender. RTCP packets are sent periodically to provide feedback on the quality of the RTP stream. They contain statistical information about the RTP stream, including the number of packets sent and received, the number of packets lost, and the delay between packets. This information can be used to adjust the RTP stream to improve the quality of the audio or video.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding video compression
&lt;/h2&gt;

&lt;p&gt;We will not delve deeply into video compression, but we will cover enough to understand why RTP is designed as it is. Video compression encodes video into a new format that requires fewer bits to represent the same video.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lossy and Lossless Compression
&lt;/h3&gt;

&lt;p&gt;Video can be encoded to be lossless (no information is lost) or lossy (information may be lost). RTP usually uses lossy compression to prevent high latency streams and more dropped packets, even if the video quality is not as good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intra and Inter-Frame Compression
&lt;/h3&gt;

&lt;p&gt;Video compression comes in two types: intra-frame and inter-frame. Intra-frame compression reduces the bits used to describe a single video frame. The same techniques are used to compress still pictures, like the JPEG compression method. On the other hand, inter-frame compression looks for ways to not send the same information twice, since video is made up of many pictures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inter-Frame types
&lt;/h3&gt;

&lt;p&gt;There are three frame types in inter-frame compression:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I-Frame&lt;/strong&gt; - A complete picture that can be decoded without anything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P-Frame&lt;/strong&gt; - A partial picture that contains only changes from the previous picture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B-Frame&lt;/strong&gt; - A partial picture that is a modification of previous and future pictures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CTMzX635--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m9rezabokjc23qva81n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CTMzX635--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m9rezabokjc23qva81n.png" alt="Video compression" width="880" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is clear that video compression is a stateful process, posing challenges when transferred over the internet. It leads us to wonder, what happens if part of an I-Frame is lost? How does a P-Frame determine what to modify? As video compression methods become more intricate, these issues become even more pressing. Nonetheless, there is a solution offered by RTP and RTCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  RTP packet structure
&lt;/h2&gt;

&lt;p&gt;Every RTP packet has the following structure, as defined in the &lt;a href="https://www.rfc-editor.org/info/rfc3550"&gt;RFC&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Synchronization Source (SSRC) identifier            |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|            Contributing Source (CSRC) identifiers             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            Payload                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Version (V)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Version&lt;/code&gt; is always set to &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Padding (P)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Padding&lt;/code&gt; is a boolean that determines whether the payload has padding or not.&lt;/p&gt;

&lt;p&gt;The last byte of the payload indicates the number of padding bytes that were added.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extension (X)
&lt;/h3&gt;

&lt;p&gt;If set, the RTP header will contain extensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSRC Count (CC)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;CSRC Count&lt;/code&gt; refers to the number of &lt;code&gt;CSRC&lt;/code&gt; identifiers following the &lt;code&gt;SSRC&lt;/code&gt; and preceding the payload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Marker
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Marker&lt;/code&gt; bit has no predetermined meaning and can be used as desired by the user.&lt;/p&gt;

&lt;p&gt;In some cases, it may indicate when a user is speaking, or it may denote a keyframe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payload Type (PT)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Payload Type&lt;/code&gt; is a unique identifier for the codec being carried by this packet.&lt;/p&gt;

&lt;p&gt;For WebRTC, the &lt;code&gt;Payload Type&lt;/code&gt; is dynamic, meaning that the &lt;code&gt;Payload Type&lt;/code&gt; for VP8 in one call may differ from that of another. The offerer in the call determines the mapping of &lt;code&gt;Payload Types&lt;/code&gt; to codecs in the &lt;code&gt;Session Description&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sequence Number
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Sequence Number&lt;/code&gt; is used for ordering packets in a stream. Every time a packet is sent, the &lt;code&gt;Sequence Number&lt;/code&gt; is incremented by one.&lt;/p&gt;

&lt;p&gt;RTP is designed to be useful over lossy networks. This gives the receiver a way to detect when packets have been lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timestamp
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Timestamp&lt;/code&gt; is the sampling instant for this packet. It is not a global clock, but rather represents how much time has passed in the media stream. Several RTP packets can have the same timestamp if, for example, they are all part of the same video frame.&lt;/p&gt;

&lt;h3&gt;
  
  
  Synchronization Source (SSRC)
&lt;/h3&gt;

&lt;p&gt;An &lt;code&gt;SSRC&lt;/code&gt; is a unique identifier for this stream. This allows multiple streams of media to be run over a single RTP stream.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contributing Source (CSRC)
&lt;/h3&gt;

&lt;p&gt;This is a list that communicates which SSRCs contributed to this packet.&lt;/p&gt;

&lt;p&gt;This is commonly used for talking indicators. For example, if multiple audio feeds are combined into a single RTP stream on the server side, this field could be used to indicate which input streams were active at a given moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payload
&lt;/h3&gt;

&lt;p&gt;This field contains the actual payload data, which may end with the count of how many padding bytes were added if the padding flag is set.&lt;/p&gt;

&lt;h2&gt;
  
  
  RTCP packet structure
&lt;/h2&gt;

&lt;p&gt;Every RTCP packet has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|    RC   |       PT      |             length            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            Payload                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Version (V)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Version&lt;/code&gt; is always &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Padding (P)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Padding&lt;/code&gt; is a boolean that controls the inclusion of padding in the payload.&lt;/p&gt;

&lt;p&gt;The last byte of the payload includes a count of the added padding bytes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reception Report Count (RC)
&lt;/h3&gt;

&lt;p&gt;This indicates the number of reports included in this packet. A single RTCP packet may contain multiple events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packet Type (PT)
&lt;/h3&gt;

&lt;p&gt;This is a unique identifier for the type of RTCP packet. While a WebRTC agent does not necessarily need to support all these types, there may be differences in support between agents. Some of the commonly seen packet types include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;192&lt;/code&gt; - Full INTRA-frame Request (&lt;code&gt;FIR&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;193&lt;/code&gt; - Negative Acknowledgements (&lt;code&gt;NACK&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;200&lt;/code&gt; - Sender Report&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;201&lt;/code&gt; - Receiver Report&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;205&lt;/code&gt; - Generic RTP Feedback&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;206&lt;/code&gt; - Payload Specific Feedback (including &lt;code&gt;PLI&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  RTCP packet types in detail
&lt;/h2&gt;

&lt;p&gt;RTCP is a flexible protocol and supports several types of packets. The following details some of the most commonly used packet types.&lt;/p&gt;

&lt;h3&gt;
  
  
  PLI (Picture Loss Indication)/FIR (Full Intra Request)
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;FIR&lt;/code&gt; and &lt;code&gt;PLI&lt;/code&gt; messages serve a similar purpose, requesting a full key frame from the sender. However, &lt;code&gt;PLI&lt;/code&gt; is specifically used when the decoder failed to decode partial frames, which could be due to packet loss or decoder crashing.&lt;/p&gt;

&lt;p&gt;As per RFC 5104, &lt;code&gt;FIR&lt;/code&gt; should not be used when packets or frames are lost; that is the job of &lt;code&gt;PLI&lt;/code&gt;. &lt;code&gt;FIR&lt;/code&gt; is used to request a key frame for other reasons, such as when a new member enters a video conference and needs a full key frame to start decoding the video stream. The decoder will discard frames until a key frame arrives.&lt;/p&gt;

&lt;p&gt;However, in practice, software that handles both &lt;code&gt;PLI&lt;/code&gt; and &lt;code&gt;FIR&lt;/code&gt; packets will send a signal to the encoder to produce a new full key frame in both cases.&lt;/p&gt;

&lt;p&gt;Typically, a receiver will request a full key frame immediately after connecting to minimize the time to the first frame appearing on the user's screen.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PLI&lt;/code&gt; packets are a part of Payload Specific Feedback messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  NACK (Negative Acknowledgement)
&lt;/h3&gt;

&lt;p&gt;When a receiver issues a &lt;code&gt;NACK&lt;/code&gt;, it requests that the sender re-transmit a single RTP packet. This is typically done when a packet is lost or delayed. The &lt;code&gt;NACK&lt;/code&gt; is preferable to requesting that the entire frame be re-sent because RTP divides packets into small pieces, and the receiver is usually only missing one piece. To request the missing piece, the receiver creates an RTCP message with the SSRC and Sequence Number. If the sender does not have the requested RTP packet to re-send, it will simply ignore the message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sender and Receiver Reports
&lt;/h3&gt;

&lt;p&gt;These reports are crucial for transmitting statistics between agents. They effectively communicate the exact amount of packets that are received as well as the levels of jitter.&lt;/p&gt;

&lt;p&gt;This feature provides valuable diagnostic information and enables effective congestion control. We’ll see more about how these reports are used to overcome unreliable network conditions below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcoming unreliable networks
&lt;/h2&gt;

&lt;p&gt;Real-time communication is heavily dependent on networks. In an ideal scenario, bandwidth would be unlimited and packets would arrive instantaneously. Unfortunately, networks are limited and conditions can change unexpectedly, making it difficult to measure and observe network performance. Additionally, different hardware, software, and configurations can cause unpredictable behavior.&lt;/p&gt;

&lt;p&gt;RTP/RTCP runs across many different types of networks, so it is common for some communication to be lost between the sender and the receiver. Because it is built on top of UDP, there is no built-in way to retransmit packets or handle congestion control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measuring and communicating network status
&lt;/h3&gt;

&lt;p&gt;RTP/RTCP operates on various network types and topologies, and communication drops may occur from sender to receiver due to this. As they are built on UDP, there is no inherent mechanism for packet retransmission or congestion control.&lt;/p&gt;

&lt;p&gt;For optimal user experience, we must assess network path qualities and adapt to their changes over time. The key traits to monitor are &lt;strong&gt;available bandwidth&lt;/strong&gt; (in each direction, which may not be symmetrical), &lt;strong&gt;round trip time&lt;/strong&gt;, and &lt;strong&gt;jitter&lt;/strong&gt; (variations in round trip time). Our systems must take into account packet loss and convey changes in these properties as network conditions change.&lt;/p&gt;

&lt;p&gt;The protocols have two main objectives:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Estimate the available bandwidth (in each direction) supported by the network.&lt;/li&gt;
&lt;li&gt;Communicate network characteristics between sender and receiver.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Receiver reports / Sender reports
&lt;/h3&gt;

&lt;p&gt;Receiver Reports and Sender Reports are sent over RTCP and are defined in &lt;a href="https://tools.ietf.org/html/rfc3550#section-6.4"&gt;RFC 3550&lt;/a&gt;. They communicate network status between endpoints. Receiver Reports communicate network quality, including packet loss, round-trip time, and jitter. These reports pair with other algorithms that estimate available bandwidth based on the network quality.&lt;/p&gt;

&lt;p&gt;Sender and Receiver Reports (SR and RR) together give a picture of network quality. They are sent on a schedule for each SSRC and are used to estimate available bandwidth. The sender estimates available bandwidth after receiving the RR data, which contains the following fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fraction Lost&lt;/strong&gt; - Percentage of packets lost since the last Receiver Report.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cumulative Number of Packets Lost&lt;/strong&gt; - Number of packets lost during the entire call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended Highest Sequence Number Received&lt;/strong&gt; - Last Sequence Number received and how many times it has rolled over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interarrival Jitter&lt;/strong&gt; - Rolling Jitter for the entire call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last Sender Report Timestamp&lt;/strong&gt; - Last known time on sender, used for round-trip time calculation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These statistics are further fed into bandwidth estimation algorithms such as GCC (Google Congestion Control), which estimate available bandwidth which in turn drives the encoding bitrate and frame resolution.&lt;/p&gt;

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

&lt;p&gt;In conclusion, RTP and RTCP are essential protocols for enabling real-time communication in WebRTC. RTP is responsible for transmitting audio and video data over the network, while RTCP is responsible for monitoring network conditions and providing feedback to the sender. Together, these protocols enable high-quality real-time communication over the internet. Understanding how RTP and RTCP work together is essential for anyone interested in developing real-time communication applications using WebRTC.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.rfc-editor.org/info/rfc3550"&gt;RFC3550&lt;/a&gt;(RTP: A Transport Protocol for Real-Time Applications)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.rfc-editor.org/info/rfc5104"&gt;RFC5104&lt;/a&gt; (Codec Control Messages in the RTP Audio-Visual Profile with Feedback)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.rfc-editor.org/info/rfc8888"&gt;RFC8888&lt;/a&gt; (RTP Control Protocol (RTCP) Feedback for Congestion Control)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try &lt;a href="https://dyte.io/"&gt;Dyte&lt;/a&gt; if you don't want to deal with the hassle of managing your own peer-to-peer connections!&lt;/p&gt;

&lt;p&gt;If you haven't heard of Dyte yet, go to &lt;a href="https://dyte.io/"&gt;https://dyte.io&lt;/a&gt; to learn how our SDKs and libraries revolutionize the live video and voice-calling experience. Don't just take our word for it; try it for yourself! Dyte offers free &lt;a href="https://dev.dyte.io/signup"&gt;10,000 minutes&lt;/a&gt; every month to get you started quickly.If you have any questions or simply want to chat with us, please contact us through &lt;a href="//mailto:support@dyte.io"&gt;support&lt;/a&gt; or visit our &lt;a href="https://community.dyte.io/"&gt;developer community forum&lt;/a&gt;. Looking forward to it!&lt;/p&gt;

</description>
      <category>webrtc</category>
      <category>rtp</category>
      <category>rtcp</category>
      <category>video</category>
    </item>
    <item>
      <title>Tools of the Trade: Topgrade</title>
      <dc:creator>Spherical Flying Kat</dc:creator>
      <pubDate>Mon, 06 Jun 2022 19:44:41 +0000</pubDate>
      <link>https://dev.to/sphericalkat/tools-of-the-trade-topgrade-3pig</link>
      <guid>https://dev.to/sphericalkat/tools-of-the-trade-topgrade-3pig</guid>
      <description>&lt;p&gt;In the third post of this series, let's talk about &lt;code&gt;topgrade&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is topgrade?
&lt;/h1&gt;

&lt;p&gt;You've probably run into this scenario before: you have a package manager for your system; &lt;code&gt;brew&lt;/code&gt; on Mac, &lt;code&gt;pacman&lt;/code&gt; on Arch Linux etc, and you have a bunch of packages installed using these package managers.&lt;/p&gt;

&lt;p&gt;Now comes the twist, you end up having several other package managers (eg: &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt;); because nobody can agree on a standard and it's probably too far gone at this point to care anyway.&lt;/p&gt;

&lt;p&gt;To keep the entirety of your system up to date, you end up having to run several commands, which eats up precious time that you could be spending watching cat videos.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;topgrade&lt;/code&gt; remedies this situation by detecting the tools and package managers installed on your system and runs the appropriate commands to update them.&lt;/p&gt;

&lt;h1&gt;
  
  
  How do I get it?
&lt;/h1&gt;

&lt;p&gt;Pretty straightforward. &lt;code&gt;topgrade&lt;/code&gt; provides packages for Arch Linux, NixOS, and Mac (via &lt;code&gt;homebrew&lt;/code&gt; or &lt;code&gt;macports&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you're on a different platform or OS, install &lt;code&gt;topgrade&lt;/code&gt; via the &lt;code&gt;cargo&lt;/code&gt; package manager by running &lt;code&gt;cargo install topgrade&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If you're a normie who doesn't use Rust, go ahead and download one of the binaries available in the &lt;a href="https://github.com/r-darwish/topgrade/releases"&gt;releases&lt;/a&gt; and place it on your &lt;code&gt;$PATH&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Usage
&lt;/h1&gt;

&lt;p&gt;Dead simple. Just run &lt;code&gt;topgrade&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://asciinema.org/a/499879"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rKfDHqoy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asciinema.org/a/499879.svg" alt="asciicast" width="880" height="897"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it from me. Enjoy those cat videos!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>opensource</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Tools of the Trade: Declarative DNS with dnscontrol</title>
      <dc:creator>Spherical Flying Kat</dc:creator>
      <pubDate>Fri, 27 May 2022 02:13:04 +0000</pubDate>
      <link>https://dev.to/sphericalkat/tools-of-the-trade-declarative-dns-with-dnscontrol-21n</link>
      <guid>https://dev.to/sphericalkat/tools-of-the-trade-declarative-dns-with-dnscontrol-21n</guid>
      <description>&lt;h2&gt;
  
  
  💡 A bit of context
&lt;/h2&gt;

&lt;p&gt;I recently moved my &lt;a href="https://sphericalk.at" rel="noopener noreferrer"&gt;site&lt;/a&gt;'s hosting from DigitalOcean to &lt;a href="https://fly.io" rel="noopener noreferrer"&gt;fly.io&lt;/a&gt;. That's when I realized that I've been changing DNS records manually for years; go to the Cloudflare dashboard, change records, rinse-repeat...&lt;/p&gt;

&lt;p&gt;As a programmer and self-professed automation fanatic, this rubbed me the wrong way, so I set out to do something about it. Enter DNSControl.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Hold up, what's that?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://stackexchange.github.io/dnscontrol/" rel="noopener noreferrer"&gt;DNSControl&lt;/a&gt; is an &lt;strong&gt;opinionated&lt;/strong&gt; platform for seamlessly managing your DNS configuration across any number of DNS hosts, both in the cloud or in your own infrastructure.&lt;/p&gt;

&lt;p&gt;It was created by StackOverflow to manage their own domains, and was subsequently open-sourced for the rest of us (Thanks StackOverflow!).&lt;/p&gt;

&lt;p&gt;It lets you &lt;em&gt;declare&lt;/em&gt; your DNS records using a JavaScript-ish DSL (domain specific language); for example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofqaevh7g2vd0d1xidrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofqaevh7g2vd0d1xidrp.png" alt="Example of a DNS configuration using dnscontrol"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Okay, why should I switch?
&lt;/h2&gt;

&lt;p&gt;Switching to a new tool can be scary, but here's some reasons that make DNScontrol unrivaled (in my opinion):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supports 10+ DNS Providers including BIND, AWS Route 53, Google DNS, Cloudflare, and name.com&lt;/li&gt;
&lt;li&gt;Enable/disable Cloudflare proxying (the "orange cloud" button) directly from your DNSControl files.&lt;/li&gt;
&lt;li&gt;Apply CI/CD principles to DNS: Unit-tests, system-tests, automated deployment.&lt;/li&gt;
&lt;li&gt;Super extensible! Plug-in architecture makes adding new DNS providers and Registrars easy!&lt;/li&gt;
&lt;li&gt;Eliminate vendor lock-in. Switch DNS providers easily, any time, with full fidelity.&lt;/li&gt;
&lt;li&gt;All the benefits of Git (or any VCS) for your DNS zone data. View history. Accept PRs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And much more!&lt;/p&gt;

&lt;h2&gt;
  
  
  🤨 Wait! I heard opinionated?
&lt;/h2&gt;

&lt;p&gt;That's right. DNSControl has opinions about how to manage DNS. However, that isn't a bad thing. Let's go over their opinions and see which ones really affect us.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DNS should be treated like code:
Code is written in a high-level language, version controlled, commented, tested, and reviewed by a third party… and all of that happens before it goes into production.&lt;/li&gt;
&lt;li&gt;Non-experts should be able to safely make DNS changes.&lt;/li&gt;
&lt;li&gt;All DNS is lowercase for languages that have such a concept.&lt;/li&gt;
&lt;li&gt;Users should state what they want, and DNSControl should do the rest.&lt;/li&gt;
&lt;li&gt;If it is ambiguous in DNS, it is forbidden in DNSControl.&lt;/li&gt;
&lt;li&gt;Hostnames don’t have underscores.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These make total sense. To read more about the opinions and the reasons behind them, check out the &lt;a href="https://stackexchange.github.io/dnscontrol/opinions" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Okay, I'm convinced. How do I begin?
&lt;/h2&gt;

&lt;p&gt;Glad to hear it! To get started, first install DNSControl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go install github.com/StackExchange/dnscontrol/v3@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create a file called &lt;code&gt;dnsconfig.js&lt;/code&gt;. This will house all the records for your site(s). Populate it with the starter boilerplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Providers&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;REG_NONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NewRegistrar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// No registrar.&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;DNS_BIND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NewDnsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bind&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ISC BIND.&lt;/span&gt;

&lt;span class="c1"&gt;// Domains&lt;/span&gt;
&lt;span class="nc"&gt;D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;REG_NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DnsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DNS_BIND&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.2.3.4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a file called &lt;code&gt;creds.json&lt;/code&gt; for storing provider configurations (API tokens and other account information). For example, to use both &lt;code&gt;name.com&lt;/code&gt; and &lt;code&gt;Cloudflare&lt;/code&gt;, you would have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cloudflare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CLOUDFLAREAPI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"accountid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-cloudflare-account-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apitoken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-cloudflare-api-token"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"namecom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NAMEDOTCOM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apikey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apiuser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NONE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no-op&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;provider&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a complete list of providers and how to use them, check out the &lt;a href="https://stackexchange.github.io/dnscontrol/provider-list" rel="noopener noreferrer"&gt;provider docs&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: Remember to add &lt;code&gt;creds.json&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; committing and pushing your configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, run &lt;code&gt;dnscontrol preview&lt;/code&gt; and make sure that it completes with no errors. The preview command is the “dry run” mode that shows what changes need to be made and never makes any actual changes. It will use APIs if needed to find out what DNS entries currently exist.&lt;/p&gt;

&lt;p&gt;The results should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dnscontrol preview
Initialized 1 registrars and 1 dns service providers.
******************** Domain: example.com
----- Getting nameservers from: bind
----- DNS Provider: bind... 1 correction
#1: GENERATE_ZONEFILE: example.com
 (2 records)

----- Registrar: none
Done. 1 corrections.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, run &lt;code&gt;dnscontrol push&lt;/code&gt; to actually make the changes. In this case, the change will be to create a zone file where one didn’t previously exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dnscontrol push
Initialized 1 registrars and 1 dns service providers.
******************** Domain: example.com
----- Getting nameservers from: bind
----- DNS Provider: bind... 1 correction
#1: GENERATE_ZONEFILE: example.com
 (2 records)

CREATING ZONEFILE: zones/example.com.zone
SUCCESS!
----- Registrar: none
Done. 1 corrections.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ⚙️ I already have many DNS records set up. How do I import them?
&lt;/h2&gt;

&lt;p&gt;This was a problem I faced myself, and this is how you import your existing DNS configurations.&lt;/p&gt;

&lt;p&gt;Most DNS Service Providers have an 'export to zonefile' feature. (If yours doesn't, consider moving to Cloudflare; It's free, they'll import all your records during setup, and will let you export a zonefile)&lt;/p&gt;

&lt;p&gt;Place this zonefile inside a directory called &lt;code&gt;zones&lt;/code&gt; in the directory where your &lt;code&gt;dnsconfig.js&lt;/code&gt; resides. Rename the zonefile to &lt;code&gt;&amp;lt;domain&amp;gt;.zone&lt;/code&gt;. Your directory structure should now look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── creds.json
├── dnsconfig.js
└── zones
    └── example.com.zone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dnscontrol get-zones --format=js --out=draft.js bind BIND example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can copy the contents of &lt;code&gt;draft.js&lt;/code&gt; to &lt;code&gt;dnsconfig.js&lt;/code&gt; and modify as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Lastly, some advice for running in production
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Store the configuration files in Git.&lt;/li&gt;
&lt;li&gt;Encrypt or completely omit the &lt;code&gt;creds.json&lt;/code&gt; file before storing it in Git. Do NOT store API keys or other credentials without encrypting them.&lt;/li&gt;
&lt;li&gt;Use a CI/CD tool like Github Actions/Jenkins/CircleCI/etc. to automatically push DNS changes.&lt;/li&gt;
&lt;li&gt;Join the DNSControl community. File &lt;a href="https://github.com/StackExchange/dnscontrol" rel="noopener noreferrer"&gt;issues and PRs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this tool is just as useful to you as it is to me. Stay tuned for further posts about useful tooling.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>dns</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tools of the Trade: Dyte CLI</title>
      <dc:creator>Spherical Flying Kat</dc:creator>
      <pubDate>Thu, 05 May 2022 13:14:19 +0000</pubDate>
      <link>https://dev.to/sphericalkat/tools-of-the-trade-dyte-cli-52ia</link>
      <guid>https://dev.to/sphericalkat/tools-of-the-trade-dyte-cli-52ia</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qfKX19IB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/0%2AP5SmQan2HVKl5IV3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qfKX19IB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/0%2AP5SmQan2HVKl5IV3.jpg" alt="header" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dyte CLI brings &lt;a href="https://dyte.io"&gt;Dyte&lt;/a&gt; to your terminal. It reduces context switching, helps you focus, and empowers you to more easily script and create your own workflows on top of Dyte.&lt;/p&gt;

&lt;p&gt;With our CLI, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run your entire Dyte workflow from the terminal, from meetings through recordings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Play around with webhooks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Release your own custom plugins&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎬 From meetings to recordings
&lt;/h2&gt;

&lt;p&gt;The inherent scriptability brought to you by a CLI lets you chain many logical actions together. For example, you could have a workflow like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re an ed-tech startup and have a quick extra session scheduled for your students. Let’s create a meeting in a jiffy:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_vg8i1JO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3718/0%2AAIqkeJokFrTcSUan.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_vg8i1JO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3718/0%2AAIqkeJokFrTcSUan.jpeg" alt="Create meeting UX" width="880" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now that you have a meeting to work with, you can share the link with your students and have your session 🤝.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You’ve noticed some people are tardy or completely skip extra sessions. Not good 😠. What can we do? We can keep a track of who joins using webhooks!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xYQjqabs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3718/0%2AE9I_GlZ3-mVTOe_w.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xYQjqabs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3718/0%2AE9I_GlZ3-mVTOe_w.jpeg" alt="Create webhook UX" width="880" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oh, the session is very important, you should record this!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZMt7u0Qd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3718/0%2Au9LFrUCYf022mV0b.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZMt7u0Qd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3718/0%2Au9LFrUCYf022mV0b.jpeg" alt="Start Recording UX" width="880" height="970"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s just an example of how you can make Dyte CLI your own!&lt;/p&gt;

&lt;h2&gt;
  
  
  🎨 Design considerations
&lt;/h2&gt;

&lt;p&gt;While a CLI might not have a lot of snazzy eye-candy like a &lt;a href="https://dyte.io"&gt;website&lt;/a&gt; or an app, the design that goes into it is just as important.&lt;/p&gt;

&lt;p&gt;We kept a lot of things in mind while designing the CLI so that our end-users have the best possible experience while using it. Here’s a few of the things that went on in our heads while we were working on it:&lt;/p&gt;

&lt;h3&gt;
  
  
  #️⃣ POSIX compliance
&lt;/h3&gt;

&lt;p&gt;Unix-like operating systems popularised the use of the command line and tools such as awkand sed. Such tools have effectively standardised the behaviour of command line options (aka flags), options-arguments, and other operands.&lt;/p&gt;

&lt;p&gt;Why is it important? Users might get confused if our CLI’s syntax for arguments, options, or command parameters deviate from the de facto Unix standards they are used to.&lt;/p&gt;

&lt;p&gt;Some examples of expected behaviour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Option-arguments or options can be notated in help or examples as square brackets ([]) to indicate they are optional, or with angle brackets (&amp;lt;&amp;gt;) to indicate they are required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allowing short-form single letter arguments as aliases for long-form arguments (see reference from the &lt;a href="https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html"&gt;GNU Coding Standards&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;specifying multiple options with no values may be grouped, such as dyte -abc being equivalent to dyte -a -b -c.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💕 Empathy for the user
&lt;/h3&gt;

&lt;p&gt;We’ve put workflows in place that assist the user in interacting with the CLI successfully. We understand the frustration that comes with being unable to use an unfamiliar tool effectively, and we’re here to help you through it!&lt;/p&gt;

&lt;p&gt;As we’ve seen while starting a recording, the start subcommand takes in a meeting ID. Failing to pass in one and being presented with an error would get pretty frustrating!&lt;/p&gt;

&lt;p&gt;And so, we’ve taken care to prompt the user gently for any parameters they miss out on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5XSp3wn2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2160/0%2AkPylpV_djzi8KXnQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5XSp3wn2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2160/0%2AkPylpV_djzi8KXnQ.jpeg" alt="Prompting the user" width="880" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✨ Rich interactions and a colourful experience
&lt;/h3&gt;

&lt;p&gt;We’ve leveraged rich command-line interactions beyond that of plain text input to provide a smoother experience for our users.&lt;/p&gt;

&lt;p&gt;The CLI makes heavy use of prompts, drop-down selections, hidden password inputs, auto-complete and searchable lists.&lt;/p&gt;

&lt;p&gt;Moving on to colour, most terminals used today to interact with command line applications support coloured text. We’ve made use of this to highlight important bits of output, and to draw your attention to things that might otherwise get lost in an otherwise text-heavy experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--geb9IoY6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2468/0%2Age0VCC_qboyuPyod.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--geb9IoY6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2468/0%2Age0VCC_qboyuPyod.jpeg" alt="Important information is highlighted in colour" width="880" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Stateful data
&lt;/h3&gt;

&lt;p&gt;We’ve strived to provide a stateful experience, by remembering values and data where we can to improve future interactions.&lt;/p&gt;

&lt;p&gt;This is important to ensure we don’t bug the user to enter the same information again and again.&lt;/p&gt;

&lt;p&gt;All data stored by the CLI can be found at ~/.config/dyte&lt;/p&gt;

&lt;h3&gt;
  
  
  🧹 Cleaning up
&lt;/h3&gt;

&lt;p&gt;We’re sad to see you go, but look forward to seeing you again! We don’t leave any configuration files behind when you uninstall the CLI by leveraging the convenient postuninstall &lt;a href="https://docs.npmjs.com/misc/scripts"&gt;script&lt;/a&gt; that NPM provides.&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 Zero dependencies
&lt;/h3&gt;

&lt;p&gt;Okay, that was a bit misleading. We certainly use a few dependencies to make the CLI great, we’re not &lt;em&gt;that&lt;/em&gt; good 😛.&lt;/p&gt;

&lt;p&gt;What we do however, is bundle all these dependencies, and our own code into a single JS file using the nifty &lt;a href="https://github.com/vercel/ncc"&gt;ncc&lt;/a&gt; tool (thanks &lt;a href="https://vercel.com"&gt;Vercel&lt;/a&gt;!)&lt;/p&gt;

&lt;p&gt;This lets us ship the CLI without having to worry about any breaking changes in dependencies or nasty supply-chain attacks (remember left-pad and colors.js?). This way, you get exactly what our CI systems build and publish, Untouched by Human Hands™️&lt;/p&gt;

&lt;h2&gt;
  
  
  🔥 Where can I get it?
&lt;/h2&gt;

&lt;p&gt;The Dyte CLI is currently available on the &lt;a href="https://www.npmjs.com/package/@dytesdk/cli"&gt;NPM registry&lt;/a&gt;. Please also take a moment to check out how to use the CLI in our comprehensive &lt;a href="https://docs.dyte.io/cli/installation"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’re also planning on open-sourcing the CLI soon, so keep a watch out for any announcements in that space and feel free to contribute!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;We’ve tried to make the usage of Dyte’s CLI to be as pleasant and painless as possible. Using it, you can automate an end-to-end workflow without having to interact with dashboards or our API directly.&lt;/p&gt;

&lt;p&gt;We’ve already been using the CLI internally for months now, so bugs should be few and far between. Even so, if you find any, please report them to us!&lt;/p&gt;

&lt;p&gt;Take your Dyte experience to new heights with the CLI!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.dyte.io/tools-of-the-trade-dyte-cli/"&gt;https://blog.dyte.io&lt;/a&gt; on May 5, 2022.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>terminal</category>
    </item>
  </channel>
</rss>
