<?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: Rocky Warren</title>
    <description>The latest articles on DEV Community by Rocky Warren (@therockstorm).</description>
    <link>https://dev.to/therockstorm</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%2F997427%2F830b42e6-c9b8-4302-86d2-add02b2e6811.jpeg</url>
      <title>DEV Community: Rocky Warren</title>
      <link>https://dev.to/therockstorm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/therockstorm"/>
    <language>en</language>
    <item>
      <title>AWS SQS Best Practices</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Sat, 20 May 2023 20:44:55 +0000</pubDate>
      <link>https://dev.to/therockstorm/sqs-best-practices-46o7</link>
      <guid>https://dev.to/therockstorm/sqs-best-practices-46o7</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;AWS &lt;a href="https://aws.amazon.com/sqs/"&gt;&lt;strong&gt;Simple Queue Service&lt;/strong&gt;&lt;/a&gt; (&lt;strong&gt;SQS&lt;/strong&gt;) provides fully managed message queuing for microservices, distributed systems, and serverless applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Consume messages in a separate thread or process
&lt;/h3&gt;

&lt;p&gt;Node is single threaded; it can only operate on one task at a time. Creating a separate process creates isolation, preventing API availability issues if, for example, consumers spike CPU or memory usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consumers should be idempotent
&lt;/h3&gt;

&lt;p&gt;Both EventBridge and SQS provide “at-least once” message delivery. That means duplicate messages &lt;strong&gt;WILL&lt;/strong&gt;, at some point, happen. Consumers must handle this gracefully. A simple way to avoid duplicate writes is to use producer-provided IDs that consumers use to deduplicate messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Process messages quickly or set the proper configuration
&lt;/h3&gt;

&lt;p&gt;From &lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/working-with-messages.html#processing-messages-timely-manner"&gt;AWS’s SQS Best Practices&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Setting the visibility timeout depends on how long it takes your application to process and delete a message. For example, if your application requires 10 seconds to process a message and you set the visibility timeout to 15 minutes, you must wait for a relatively long time to attempt to process the message again if the previous processing attempt fails. Alternatively, if your application requires 10 seconds to process a message but you set the visibility timeout to only 2 seconds, a duplicate message is received by another consumer while the original consumer is still working on the message.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If using the &lt;a href="https://github.com/bbc/sqs-consumer"&gt;sqs-consumer&lt;/a&gt; library and you know how long it takes to process a message batch, configure the &lt;a href="https://bbc.github.io/sqs-consumer/interfaces/ConsumerOptions.html#visibilityTimeout"&gt;visibility timeout&lt;/a&gt;. Otherwise, configure the &lt;a href="https://bbc.github.io/sqs-consumer/interfaces/ConsumerOptions.html#heartbeatInterval"&gt;heartbeat interval&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure a &lt;strong&gt;dead-letter queue&lt;/strong&gt; (&lt;strong&gt;DLQ&lt;/strong&gt;) with maximum message retention (14 days)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/working-with-messages.html#processing-messages-timely-manner"&gt;Details&lt;/a&gt;. If a consumer fails to process a message N number of times (configured via SQS’s &lt;code&gt;maxReceiveCount&lt;/code&gt;), the message is sent to the DLQ. This prevents “poison pill” messages from continuously failing, which negatively affects consumer throughput.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid automatically consuming DLQs
&lt;/h3&gt;

&lt;p&gt;If you consume DLQ messages and delete them, they’re gone forever. Instead, set the maximum 14 day message retention. This provides a safe place for messages to sit while you root cause the issue causing messages to end up there. Once a fix is deployed, you can then consume DLQ messages to bring your service’s data up-to-date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle partial batch responses
&lt;/h3&gt;

&lt;p&gt;If your handler returns without error, sqs-consumer deletes the messages from the queue. Throwing an error, however, fails an entire batch. To delete them instead, return a list of successful messages from the handler. For Lambdas, &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting"&gt;see this&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  For Lambda, set visibility timeout to 6x the function timeout and redrive policy to at least 5
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;To allow your function time to process each batch of records, set the source queue's &lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html"&gt;visibility timeout&lt;/a&gt; to at least six times the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-function-common.html#configuration-common-summary"&gt;timeout that you configure&lt;/a&gt; on your function. The extra time allows for Lambda to retry if your function is throttled while processing a previous batch.&lt;/p&gt;

&lt;p&gt;To give messages a better chance to be processed before sending them to the dead-letter queue, set the &lt;code&gt;maxReceiveCount&lt;/code&gt; on the source queue's redrive policy to at least &lt;strong&gt;5&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#events-sqs-queueconfig"&gt;Source&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/working-with-messages.html"&gt;Working with Amazon SQS messages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html"&gt;Using Lambda with Amazon SQS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sqs</category>
      <category>aws</category>
      <category>bestpractices</category>
      <category>node</category>
    </item>
    <item>
      <title>PrivacyProtect: Securely Share Passwords and Store Sensitive Files in Convenient Locations</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/outsmart-hackers-with-privacyprotect-securely-share-passwords-and-store-sensitive-files-in-convenient-locations-3m2p</link>
      <guid>https://dev.to/therockstorm/outsmart-hackers-with-privacyprotect-securely-share-passwords-and-store-sensitive-files-in-convenient-locations-3m2p</guid>
      <description>&lt;p&gt;My dad sent me a tax document as an email attachment.&lt;/p&gt;

&lt;p&gt;Email is insecure. So are most messaging services.&lt;/p&gt;

&lt;p&gt;Now Google (I use Gmail) and Microsoft (he uses Hotmail/Outlook) have my dad's juicy personal information stored; who knows where and for how long.&lt;/p&gt;

&lt;p&gt;Why is sharing sensitive information hard?&lt;/p&gt;

&lt;p&gt;The top three Google results for "send files securely" are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dropbox&lt;/li&gt;
&lt;li&gt;A service called "Send Files Securely"&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.wired.com/story/securely-share-files-online/"&gt;A Wired article&lt;/a&gt; listing seven "best bets"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Dropbox and Send Files Securely aren't &lt;strong&gt;end-to-end&lt;/strong&gt; ( &lt;strong&gt;E2E&lt;/strong&gt; ) encrypted. Neither are five of the seven on Wired's list. Of the remaining two, Firefox Send doesn't exist anymore, and iCloud offers it, but only on the newest devices on an opt-in basis. It also disables access to iCloud.com by default and comes with &lt;a href="https://support.apple.com/en-us/HT202303"&gt;other caveats&lt;/a&gt; that may discourage casual users.&lt;/p&gt;

&lt;p&gt;E2E encryption is what we want for sensitive data. Encryption "in transit," "at rest," and/or "in storage" is great, but E2E encryption is better. Without it, there are periods when your data is readable, and many companies take this opportunity to do just that. This is how handy features like in-document search work.&lt;/p&gt;

&lt;p&gt;But sometimes, we don't want Apple, Google, and the others snooping around. We have files or images for one person's eyes only. We want our private data left alone.&lt;/p&gt;

&lt;p&gt;Most password managers let you do this. But I can't convince my dad (and other family members) to use one. Requiring an account to send files or one-off secrets is a small but genuine hurdle keeping people from doing the right thing.&lt;/p&gt;

&lt;p&gt;So, when I heard about &lt;a href="https://mprimi.github.io/portable-secret/"&gt;Portable Secret&lt;/a&gt;, I smiled. A dead simple way to share and store passwords, files, and images. However, the creator, &lt;a href="https://www.mpri.me/"&gt;Marco&lt;/a&gt;, &lt;a href="https://github.com/mprimi/portable-secret/discussions/29"&gt;has little time for upkeep&lt;/a&gt;, and the security landscape changes fast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.privacyprotect.dev?utm_source=dev.to&amp;amp;utm_medium=social&amp;amp;utm_campaign=blog&amp;amp;utm_term=enter"&gt;Enter PrivacyProtect&lt;/a&gt;. Share passwords and sensitive files over email or store them in insecure locations like cloud drives using nothing more than desktop or mobile web browsers like Chrome and Safari.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JOgUqfoA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fprivacy-protect.f4ae3b15.png%26w%3D1080%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JOgUqfoA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fprivacy-protect.f4ae3b15.png%26w%3D1080%26q%3D75" alt="PrivacyProtect: share and store passwords and sensitive files with end-to-end encryption." width="512" height="512"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;PrivacyProtect: share and store passwords and sensitive files with end-to-end encryption.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No special software. No need to create an account. It's free, &lt;a href="https://github.com/therockstorm/privacy-protect"&gt;open-source&lt;/a&gt;, keeps your private data a secret, and leaves you alone.&lt;/p&gt;

&lt;p&gt;It's so easy, my dad can use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.privacyprotect.dev?utm_source=dev.to&amp;amp;utm_medium=social&amp;amp;utm_campaign=blog&amp;amp;utm_term=give-it-a-try-0"&gt;Give it a try&lt;/a&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Enter your secret &lt;strong&gt;message&lt;/strong&gt; or upload a &lt;strong&gt;file&lt;/strong&gt; you want to protect.&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;password&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Create an optional &lt;strong&gt;password hint&lt;/strong&gt; to either help you remember the password or help the recipient guess it.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Conceal and download secret&lt;/strong&gt; to generate your protected HTML file and download it. Depending on your device's speed, it can take a few seconds.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KsTthgSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fexample.7ce7fb96.png%26w%3D3840%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KsTthgSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fexample.7ce7fb96.png%26w%3D3840%26q%3D75" alt="PrivacyProtect example." width="880" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And just like that, your secret is protected!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming you chose a strong password&lt;/strong&gt; , the downloaded file is safe to share over insecure channels like email or messaging services. To view the secret, the recipient (which may be you) will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Save&lt;/strong&gt; the HTML file to their device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open&lt;/strong&gt; it in a web browser.&lt;/li&gt;
&lt;li&gt;Enter the &lt;strong&gt;password&lt;/strong&gt;. If the recipient cannot guess the password based on the hint, give them a call and let them know.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Reveal secret&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here are examples of a PrivacyProtected &lt;a href="https://www.privacyprotect.dev/example-message.html?utm_source=dev.to&amp;amp;utm_medium=social&amp;amp;utm_campaign=blog&amp;amp;utm_term=example-message"&gt;message&lt;/a&gt; and &lt;a href="https://www.privacyprotect.dev/example-image.html?utm_source=dev.to&amp;amp;utm_medium=social&amp;amp;utm_campaign=blog&amp;amp;utm_term=example-image"&gt;image file&lt;/a&gt;. Try "dog" for the password.&lt;/p&gt;

&lt;p&gt;You can also safely store the HTML file in insecure locations like cloud drives (Dropbox, iCloud, Google Drive), host it like the examples above, or keep it on your computer, phone, or USB. Even if you lose your USB drive, the secret is unreadable by anyone that finds it.&lt;/p&gt;

&lt;h2&gt;
&lt;span&gt;Get PrivacyProtect updates&lt;/span&gt;
&lt;/h2&gt;

&lt;p&gt;Get notified of new features. Unsubscribe at any time.&lt;/p&gt;

&lt;p&gt;Join&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;p&gt;PrivacyProtect is a complement, not a replacement, for password managers. We've discussed a few use cases above. Here are other ways to use it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have you sent sensitive tax documents to your CPA or bought or sold a house that required sending countless PDFs over insecure email? Use PrivacyProtect to conceal them beforehand!&lt;/li&gt;
&lt;li&gt;Do you have a crypto wallet? Store your secret recovery phrase in a PrivacyProtected file with a strong password instead of on paper under your mattress.&lt;/li&gt;
&lt;li&gt;PrivacyProtect government-issued IDs and health records before uploading them to cloud storage to keep Big Tech at bay.&lt;/li&gt;
&lt;li&gt;It's possible to lose access to the device that generates your two-factor authentication (2FA) backup codes. It happened to me, and I'm locked out of my Dropbox account forever. Don't be like me. Store backup codes for things like your password manager and primary email account in multiple locations.&lt;/li&gt;
&lt;li&gt;PrivacyProtect works on any device with a web browser, whether borrowed, brand new, or in a public library. Keep PrivacyProtected files on a USB drive and access them anywhere without installing software.&lt;/li&gt;
&lt;li&gt;A non-obvious example: if your only goal is to keep Apple, Microsoft, etc., from reading your email or file, you could use a weak password like "123" and include the password itself in the hint. It still prevents their software from reading your file.&lt;/li&gt;
&lt;li&gt;Related to the above, have you ever been pickpocketed or lost your passport in a foreign country? My friend lost his. He spent the day in the U.S. Embassy, stressed out, proving his identity. One of the items listed as "evidence of U.S. citizenship" on &lt;a href="https://travel.state.gov/content/travel/en/international-travel/emergencies/lost-stolen-passport-abroad.html"&gt;travel.state.gov&lt;/a&gt; is a photocopy of your missing passport. Skip (some of the) stress. Keep a PrivacyProtected file on your phone or host your document to access it in a pinch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're short on time, you can skip the rest of this article and &lt;a href="https://www.privacyprotect.dev?utm_source=dev.to&amp;amp;utm_medium=social&amp;amp;utm_campaign=blog&amp;amp;utm_term=start-using"&gt;start using PrivacyProtect&lt;/a&gt; immediately; it's that simple! For those interested in details, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Your secret is safe; &lt;strong&gt;it never leaves your device&lt;/strong&gt;. No data transfers to or from PrivacyProtect servers after the initial page load. In fact, you can disable your internet connection, and concealing and revealing your secret still works. PrivacyProtect doesn't know who you are, what you're sharing, or who you're sharing it with.&lt;/p&gt;

&lt;p&gt;For encryption, PrivacyProtect uses &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API"&gt;native browser APIs&lt;/a&gt; with no external dependencies. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#parameters"&gt;Argon2 doesn't have browser support&lt;/a&gt;. So to derive a key from the entered password, PrivacyProtect uses PBKDF2 with 2,100,000 iterations, a random salt, and the SHA-512 hash, &lt;a href="https://soatok.blog/2022/12/29/what-we-do-in-the-etc-shadow-cryptography-with-passwords/"&gt;as recommended here&lt;/a&gt;. This iteration count is ten times the &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2"&gt;OWASP-recommended&lt;/a&gt; 210,000. This is justified in light of the recent &lt;a href="https://blog.lastpass.com/2022/12/notice-of-recent-security-incident/"&gt;LastPass breach&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PrivacyProtect encrypts the plaintext using &lt;a href="https://www.nist.gov/publications/advanced-encryption-standard-aes"&gt;NIST-approved&lt;/a&gt; AES-256 in &lt;a href="https://csrc.nist.gov/publications/detail/sp/800-38d/final"&gt;NIST-recommended&lt;/a&gt; GCM block cipher mode using the derived key and a random initialization vector. The HTML file contains the resulting ciphertext, initialization vector, and salt needed for decryption.&lt;/p&gt;

&lt;p&gt;PrivacyProtect improves on &lt;a href="https://mprimi.github.io/portable-secret/"&gt;Portable Secret&lt;/a&gt;, provides better protection than &lt;a href="https://security.stackexchange.com/questions/35818/are-password-protected-zip-files-secure"&gt;password-protected archives&lt;/a&gt;, and is free and &lt;a href="https://github.com/therockstorm/privacy-protect"&gt;open-source&lt;/a&gt;. &lt;a href="https://www.privacyprotect.dev?utm_source=dev.to&amp;amp;utm_medium=social&amp;amp;utm_campaign=blog&amp;amp;utm_term=give-it-a-try-1"&gt;Give it a try&lt;/a&gt;; I'd love to hear your feedback!&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>passwords</category>
      <category>sharepasswords</category>
      <category>sendpasswords</category>
    </item>
    <item>
      <title>Connecting to Private RDS Databases From Your Local Machine</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Tue, 17 Jan 2023 17:14:10 +0000</pubDate>
      <link>https://dev.to/therockstorm/connecting-to-private-rds-databases-from-your-local-machine-100b</link>
      <guid>https://dev.to/therockstorm/connecting-to-private-rds-databases-from-your-local-machine-100b</guid>
      <description>&lt;p&gt;The following shows you how to connect to Relational Database Service (RDS) database instances in private Virtual Private Cloud (VPC) subnets from a local machine using EC2 bastion hosts and AWS Systems Manager (SSM) instead of using SSH directly. It assumes,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have an EC2 instance running.&lt;/li&gt;
&lt;li&gt;You have security groups configured so the EC2 instance has access to the RDS database.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If either of those isn't the case, follow those two steps in &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/rds-connect-ec2-bastion-host/"&gt;this article&lt;/a&gt; and then head back here.&lt;/p&gt;

&lt;p&gt;If you let AWS store your database secrets in &lt;strong&gt;Secrets Manager&lt;/strong&gt; when creating the database, all connection information is in the secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  From the AWS Console
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;From the &lt;strong&gt;Secrets Manager Console&lt;/strong&gt;, select to database secret.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Retrieve secret value&lt;/strong&gt;, you'll need &lt;strong&gt;host&lt;/strong&gt;, &lt;strong&gt;dbname&lt;/strong&gt;, &lt;strong&gt;username&lt;/strong&gt;, and &lt;strong&gt;password&lt;/strong&gt; in the following steps.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ABiV6hy3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5gn0szlt4jq91bc1has8.png" alt="Secrets Manager" width="880" height="548"&gt;
&lt;/li&gt;
&lt;li&gt;From the &lt;strong&gt;EC2 Console&lt;/strong&gt;, connect to the EC2 instance with access to your database using &lt;strong&gt;Session Manager&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;From the launched terminal, run the following. This is specific to PostgreSQL, but you run equivalent commands for your database.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Install postgresql&lt;/span&gt;
   &lt;span class="nb"&gt;sudo &lt;/span&gt;amazon-linux-extras &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; postgresql14

   &lt;span class="c"&gt;# Connect using the values copied in step 2&lt;/span&gt;
   &lt;span class="c"&gt;# After entering it, you'll be prompted for the password&lt;/span&gt;
   psql &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR_HOST] &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR_DB_NAME] &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR_USERNAME] &lt;span class="nt"&gt;-W&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="c1"&gt;-- List tables&lt;/span&gt;
   &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;

   &lt;span class="c1"&gt;-- Run your favorite queries&lt;/span&gt;
   &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;YOUR_TABLE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're connected! But we can do better.&lt;/p&gt;

&lt;h2&gt;
  
  
  From the command line
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run the following to see if you have &lt;code&gt;session-manager-plugin&lt;/code&gt; installed. If you don't, &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html#install-plugin-macos-signed"&gt;run the commands here&lt;/a&gt; to install it.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   session-manager-plugin &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;To start the SSM session, you need the EC2 instance ID. You can either get it from the AWS Console, or run the following with appropriate filters if you have lots of instances. The following assumes you have &lt;a href="https://stedolan.github.io/jq/"&gt;jq&lt;/a&gt; installed and have instance tags.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws ec2 describe-instances &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR_PROFILE] &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tag:[YOUR_TAG_KEY],Values&lt;span class="o"&gt;=[&lt;/span&gt;YOUR_TAG_VALUE] &lt;span class="se"&gt;\&lt;/span&gt;
     | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Reservations[].Instances[].InstanceId'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Given the EC2 instance ID, run the following to start the session,
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws ssm start-session &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR_PROFILE] &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR_INSTANCE_ID]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're connected from your own terminal! Now, run the commands in step 4 above to connect to your database and start executing queries.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>rds</category>
      <category>vpc</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Getting MMS Working on GrapheneOS With Google Fi</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Fri, 13 Jan 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/getting-mms-working-on-grapheneos-with-google-fi-25nk</link>
      <guid>https://dev.to/therockstorm/getting-mms-working-on-grapheneos-with-google-fi-25nk</guid>
      <description>&lt;p&gt;I recently installed &lt;a href="https://grapheneos.org/"&gt;GrapheneOS&lt;/a&gt; on my Pixel and enjoy the privacy protection. I use Google Fi and could send MMS messages just fine. But when someone else in the group would send a message, I would see the error "MMS delivery failure; please contact Google Fi Support". Google Fi tech support was unable to help since I'm using a non-standard version of Android. Here's the solution.&lt;/p&gt;

&lt;p&gt;First, I left the owner profile untouched and installed Sandboxed Google Play into a new profile; this will not work. When you attempt to activate Google Fi, you'll see the error "Account access only." From &lt;a href="https://support.google.com/fi/answer/6260066?hl=en&amp;amp;authuser=0"&gt;this support page&lt;/a&gt;, a troubleshooting step mentions that only the device owner can activate Google Fi service.&lt;/p&gt;

&lt;p&gt;Installing Sandboxed Google Play onto the owner profile got me further, but I'd see "Your SIM card couldn't be read." The final piece to the puzzle is to go to &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Network &amp;amp; internet&lt;/strong&gt; and toggle on the &lt;strong&gt;Enable privileged eSIM management&lt;/strong&gt; setting. Now that I know what to search for, I found &lt;a href="https://discuss.grapheneos.org/d/2281-questions-new-user-in-advance-esim-privileged-management"&gt;this discussion thread&lt;/a&gt; and &lt;a href="https://grapheneos.org/usage#sandboxed-google-play-esim"&gt;this documentation&lt;/a&gt; explaining this in more detail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tEaMMtsm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fsettings.2a8a4ce3.jpg%26w%3D3840%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tEaMMtsm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fsettings.2a8a4ce3.jpg%26w%3D3840%26q%3D75" alt="Android settings" width="880" height="1292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once toggled, open the Google Fi app to complete activation. If you're still having issues, this these steps,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Google Fi's &lt;strong&gt;App Info&lt;/strong&gt; &amp;gt; &lt;strong&gt;Storage &amp;amp; cache&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Clear cache&lt;/strong&gt; and &lt;strong&gt;Clear storage&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Back up a screen and press &lt;strong&gt;Force stop&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;System&lt;/strong&gt; &amp;gt; &lt;strong&gt;Reset options&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Erase downloaded SIMs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reboot the device&lt;/li&gt;
&lt;li&gt;Open the Google Fi app and activate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once activated, you can toggle &lt;strong&gt;Enable privileged eSIM management&lt;/strong&gt; back off. I hope this helps. Enjoy MMS and group texting!&lt;/p&gt;

</description>
      <category>grapheneos</category>
      <category>googlefi</category>
      <category>mms</category>
      <category>pixel</category>
    </item>
    <item>
      <title>A (Relatively Easy To Understand) Primer on Multi-Party Computation</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Thu, 27 Oct 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/a-relatively-easy-to-understand-primer-on-multi-party-computation-4n58</link>
      <guid>https://dev.to/therockstorm/a-relatively-easy-to-understand-primer-on-multi-party-computation-4n58</guid>
      <description>&lt;h2&gt;
  
  
  Background: digital signatures
&lt;/h2&gt;

&lt;p&gt;Blockchains use digital signatures, which use key pairs: a  &lt;strong&gt;private key&lt;/strong&gt;  (&lt;strong&gt;PK&lt;/strong&gt;) and a public key. Through digital signatures, any person with a PK can sign transactions to spend digital currencies and, assuming the PK has access, control smart contracts. Therefore, it is crucial to safeguard PKs. Some users safeguard PKs themselves and accept the risk of theft or loss. Others trust online wallets or exchanges to protect their keys.&lt;/p&gt;

&lt;p&gt;In both these options, the user puts all their trust in a single entity. In contrast, &lt;strong&gt;threshold signature schemes&lt;/strong&gt; (&lt;strong&gt;TSS&lt;/strong&gt;) require a threshold of at least two cooperating participants to produce a signature.&lt;/p&gt;

&lt;p&gt;Both &lt;strong&gt;multi-party computation&lt;/strong&gt;  (&lt;strong&gt;MPC&lt;/strong&gt;) and multi-signature, or multi-sig, offer these capabilities. The resulting MPC signature is interchangeable with a single-signer signature. However, with multi-sig, each party has a PK. This slight difference significantly impacts the cost, speed, and availability on blockchains. For availability specifically, each blockchain requires multi-sig support either natively or via smart contracts. Supporting these increases development efforts for each new blockchain.&lt;/p&gt;

&lt;h2&gt;
  
  
  MPC overview
&lt;/h2&gt;

&lt;p&gt;MPC is a sub-field of cryptography allowing parties to jointly compute a function over their inputs while keeping those inputs private. Unlike traditional cryptographic tasks that assure the security and integrity of communication and where the adversary is an outside party, this model protects party member privacy from each other.&lt;/p&gt;

&lt;p&gt;The fundamental goals of an MPC protocol are,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Input privacy: one cannot infer any information about the private data held by the parties from the messages sent during protocol execution.&lt;/li&gt;
&lt;li&gt;Correctness: any subset of colluding parties willing to share information or deviate from protocol execution instructions should not be able to force honest parties to output an incorrect result. This goal comes in two flavors: either the legitimate parties are guaranteed to compute the correct output (a "robust" protocol), or they abort if they find an error (a protocol "with abort").&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A threshold signing algorithm has three phases,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate the key pair, split the PK into multiple secret shares, and distribute these shares between parties. Distributed key generation ensures each node only learns its share; the full key is never assembled or available in one place.&lt;/li&gt;
&lt;li&gt;Gather a threshold of 

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;tt&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 parties and run an MPC protocol to sign the transaction.&lt;/li&gt;
&lt;li&gt;Verify the signature using standard signature verification algorithms.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A naive example
&lt;/h3&gt;

&lt;p&gt;Three coworkers, Alice, Bob, and Carol, want to know their average salary without disclosing their salaries. The function they wish to compute jointly, then, is, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;AVG(SA,SB,SC)AVG(S_A, S_B, S_C)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="mord mathnormal"&gt;V&lt;/span&gt;&lt;span class="mord mathnormal"&gt;G&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;B&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;C&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/p&gt;

&lt;p&gt;If they had a trusted friend, Dave, who they knew could keep a secret, they could each tell him their salary. Dave could compute the average and tell all of them. The goal of MPC is to design a protocol where, by exchanging messages only with each other, Alice, Bob, and Carol can still learn the average without revealing who makes what and without relying on Dave.&lt;/p&gt;

&lt;p&gt;Alice picks a random number known only to her and adds it to her salary. She then securely passes the result to Bob. Bob adds his salary to the first result and gives the result to Carol. Carol does the same and returns the result to Alice. Alice then subtracts the random number from the total and calculates the average salary. The parties can only learn their input and the output, the exact information they would learn if trusting their honest friend, Dave.&lt;/p&gt;

&lt;p&gt;That's MPC in a nutshell. In practice, it's far more complex for various reasons (such as robustness in the face of collusion and malicious actors).&lt;/p&gt;

&lt;h3&gt;
  
  
  Example use cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Distributed voting&lt;/li&gt;
&lt;li&gt;Private bidding and auctions&lt;/li&gt;
&lt;li&gt;Sharing of signature or decryption functions&lt;/li&gt;
&lt;li&gt;Private information retrieval (allowing a user to retrieve an item from a server in possession of a database without revealing which item was retrieved)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key pair generation with secret sharing
&lt;/h3&gt;

&lt;p&gt;A system is not decentralized if one entity controls a client's PK. MPC nodes, or nodes for short, own only one piece of a PK, known as a key share. The nodes then work together using an MPC protocol to perform necessary computations.&lt;/p&gt;

&lt;p&gt;Secret sharing protects against node failures. Secret sharing is a method for distributing a secret among a group so that no individual holds any intelligible information about the secret. However, a sufficient number of individuals may combine their shares to reconstruct the secret. Any group of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;tt&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 (for &lt;em&gt;threshold&lt;/em&gt;) or more can together reconstruct the secret, but no group of fewer than 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;tt&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 can. Such a system is called a 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;(t,n)(t, n)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
-threshold scheme.&lt;/p&gt;

&lt;p&gt;Consider the secret sharing scheme where 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;XX&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;X&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is the secret to be shared, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;PiP_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
​ are public asymmetric encryption keys, and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;QiQ_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;Q&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 are their corresponding PKs. Each party 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;JJ&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;J&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is provided with 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;{P1(P2(...(PN(X)))),Qj}\{P_1 (P_2 (...(P_N (X )))), Q_j\}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;{&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;...&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;X&lt;/span&gt;&lt;span class="mclose"&gt;))))&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;Q&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. In this scheme, any party with PK one can remove the outer layer of encryption, a party with PKs one and two can remove the first and second layers, and so on. A party with fewer than 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;NN&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 keys can never fully reach the secret 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;XX&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;X&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 without first needing to decrypt a public-key-encrypted blob for which they do not have the corresponding PK. This problem is currently computationally infeasible.&lt;/p&gt;

&lt;p&gt;Shamir's Secret Sharing is a popular scheme. In contrast to the scheme above, one may recover the secret with any 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;tt&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 out of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 shares instead of requiring all 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. Based on the Lagrange interpolation theorem, the essential idea is that 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;tt&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 points are enough to uniquely determine a polynomial of degree less than or equal to 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t−1t - 1&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. For instance, two points are sufficient to define a line, three are enough to represent a parabola, and so on. The method is to create a polynomial of degree 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t−1t - 1&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 with the secret as the first coefficient; pick the remaining coefficients randomly. Then, find 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 points on the curve and give one to each party. When at least 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;tt&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 out of the 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 parties reveal their points, there is sufficient information to fit a 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;(t−1)(t - 1)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
th degree polynomial, with the first coefficient being the secret. If this is unclear, &lt;a href="https://medium.com/wanchain-foundation/secure-multiparty-computation-and-shamirs-secret-sharing-on-wanchain-e502012b80ef"&gt;see this blog post&lt;/a&gt; for an alternate explanation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hRoqJFLo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fpolynomials.f106fddb.jpg%26w%3D3840%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hRoqJFLo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.rocky.dev/_next/image%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fpolynomials.f106fddb.jpg%26w%3D3840%26q%3D75" alt="It’s possible to draw an infinite number of polynomials of degree two through two points. Three points are required to determine it uniquely." width="880" height="880"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;It’s possible to draw an infinite number of polynomials of degree two through two points. Three points are required to determine it uniquely.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each MPC node receives only one PK share and uses it to construct partial transaction signatures to broadcast to the network. Even if a node is offline, loses PK access, or is malicious, it is still possible to reconstruct the complete transaction from the remaining nodes.&lt;/p&gt;

</description>
      <category>mpc</category>
      <category>multipartycomputation</category>
      <category>cryptography</category>
      <category>digitalsignatures</category>
    </item>
    <item>
      <title>The Importance of Cultivating a Deep Work Culture</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Fri, 07 Oct 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/the-importance-of-cultivating-a-deep-work-culture-2d3b</link>
      <guid>https://dev.to/therockstorm/the-importance-of-cultivating-a-deep-work-culture-2d3b</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rocky.dev%2F_next%2Fimage%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fdeep-work.43ab9ad3.jpg%26w%3D3840%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rocky.dev%2F_next%2Fimage%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fdeep-work.43ab9ad3.jpg%26w%3D3840%26q%3D75" alt="Person working." width="1080" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Protected "maker", "flow", or "deep work" time is important to productivity. It's difficult for larger companies to change their culture around meetings, it's not for startups. Implementing a deep work culture and fostering it as companies grow gives them a strong competitive advantage: increased per-employee productivity. This means delivering high value to customers with relatively low headcount. A low headcount, in turn, keeps communication overhead low, creating a positive flywheel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research
&lt;/h2&gt;

&lt;p&gt;Snippets from &lt;a href="http://www.paulgraham.com/makersschedule.html" rel="noopener noreferrer"&gt;Maker's Schedule, Manager's Schedule&lt;/a&gt; by Paul Graham,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are two types of schedule, which I'll call the manager's schedule and the maker's schedule. The manager's schedule is for bosses. It's embodied in the traditional appointment book, with each day cut into one hour intervals. You can block off several hours for a single task if you need to, but by default you change what you're doing every hour.&lt;/p&gt;

&lt;p&gt;When you use time that way, it's merely a practical problem to meet with someone. Find an open slot in your schedule, book them, and you're done.&lt;/p&gt;

&lt;p&gt;Most powerful people are on the manager's schedule. It's the schedule of command. But there's another way of using time that's common among people who make things, like programmers and writers. They generally prefer to use time in units of half a day at least. You can't write or program well in units of an hour. That's barely enough time to get started.&lt;/p&gt;

&lt;p&gt;When you're operating on the maker's schedule, meetings are a disaster. A single meeting can blow a whole afternoon, by breaking it into two pieces each too small to do anything hard in. Plus you have to remember to go to the meeting. That's no problem for someone on the manager's schedule. There's always something coming on the next hour; the only question is what. But when someone on the maker's schedule has a meeting, they have to think about it.&lt;/p&gt;

&lt;p&gt;I find one meeting can sometimes affect a whole day. A meeting commonly blows at least half a day, by breaking up a morning or afternoon. But in addition there's sometimes a cascading effect. If I know the afternoon is going to be broken up, I'm slightly less likely to start something ambitious in the morning. I know this may sound oversensitive, but if you're a maker, think of your own case. Don't your spirits rise at the thought of having an entire day free to work, with no appointments at all? Well, that means your spirits are correspondingly depressed when you don't. And ambitious projects are by definition close to the limits of your capacity. A small decrease in morale is enough to kill them off.&lt;/p&gt;

&lt;p&gt;Each type of schedule works fine by itself. Problems arise when they meet. Since most powerful people operate on the manager's schedule, they're in a position to make everyone resonate at their frequency if they want to. But the smarter ones restrain themselves, if they know that some of the people working for them need long chunks of time to work in.&lt;/p&gt;

&lt;p&gt;Those of us on the maker's schedule are willing to compromise. We know we have to have some number of meetings. All we ask from those on the manager's schedule is that they understand the cost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From a summary of &lt;a href="https://www.calnewport.com/books/deep-work/" rel="noopener noreferrer"&gt;Deep Work&lt;/a&gt; by Cal Newport,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deep work is the ability to focus without distraction on a cognitively demanding task. It's a skill that allows you to quickly master complicated information and produce better results in less time. Deep work will make you better at what you do and provide the sense of true fulfillment that comes from craftsmanship. In short, deep work is like a super power in our increasingly competitive twenty-first century economy. And yet, most people have lost the ability to go deep—spending their days instead in a frantic blur of e-mail and social media, not even realizing there's a better way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From a summary of &lt;a href="https://www.calnewport.com/books/a-world-without-email/" rel="noopener noreferrer"&gt;A World Without Email&lt;/a&gt; by Cal Newport (an unfortunate title, but he specifically and repeatedly mentions Slack in the book as well),&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Modern knowledge workers communicate constantly. Their days are defined by a relentless barrage of incoming messages and back-and-forth digital conversations–a state of constant, anxious chatter in which nobody can disconnect, and so nobody has the cognitive bandwidth to perform substantive work. There was a time when tools like email felt cutting edge, but a thorough review of current evidence reveals that the "hyperactive hive mind" workflow they helped create has become a productivity disaster, reducing profitability and perhaps even slowing overall economic growth. Equally worrisome, it makes us miserable. Humans are simply not wired for constant digital communication.&lt;/p&gt;

&lt;p&gt;We have become so used to an inbox-driven workday that it's hard to imagine alternatives. But they do exist. Drawing on years of investigative reporting, author and computer science professor Cal Newport makes the case that our current approach to work is broken, then lays out a series of principles and concrete instructions for fixing it. In A World without Email, he argues for a workplace in which clear processes–not haphazard messaging–define how tasks are identified, assigned and reviewed. Each person works on fewer things (but does them better), and aggressive investment in support reduces the ever-increasing burden of administrative tasks. Above all else, important communication is streamlined, and inboxes and chat channels are no longer central to how work unfolds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, from a summary of &lt;a href="https://37signals.com/books/#calm" rel="noopener noreferrer"&gt;It Doesn't Have to be Crazy at Work&lt;/a&gt; by Jason Fried and David Heinemeier Hansson,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Calm is meetings as a last resort. Calm is contextual communication. Calm is asynchronous first, real-time second. Calm is more independence, less interdependence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Startups can cultivate deep work in the following ways,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Favor asynchronous communication in ticket trackers (e.g., Trello, Linear, JIRA) over synchronous Slack.

&lt;ul&gt;
&lt;li&gt;Ticket trackers should contain all context for specific features and projects (including links to any documents and meeting notes on the subject) rather than having to search for piecemeal conversations in Slack.&lt;/li&gt;
&lt;li&gt;In this way, picking up a new story gives you everything you need to know to accomplish it. This may require breaking stories down in more detail so there are fewer implementation questions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Use &lt;a href="https://www.getclockwise.com/" rel="noopener noreferrer"&gt;Clockwise&lt;/a&gt; or similar to aid in meeting scheduling.

&lt;ul&gt;
&lt;li&gt;It takes each person's calendar into consideration to group meetings into chunks as much as possible, leaving uninterrupted blocks of time for deep work.&lt;/li&gt;
&lt;li&gt;There is an option to block off "Focus Time" on your calendar and it integrates with Slack to turn on "Do not disturb" during these times so you don't get distracted by &lt;strong&gt;Slack notification noise&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Understand this may mean delayed Slack responses. That should be okay! Allow people to remain in deep work and respond when they've come up for air.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;At least one, ideally two "No meeting days" for makers and optionally managers.

&lt;ul&gt;
&lt;li&gt;No cadence meetings scheduled on these days&lt;/li&gt;
&lt;li&gt;Slack standup instead of in-person or over video chat&lt;/li&gt;
&lt;li&gt;Impromptu meetings for blockers are okay, but try to have multiple tickets to work on so you can move onto another if you get stuck.&lt;/li&gt;
&lt;li&gt;As another option in place of "No meeting days", have bi-modal days where the mornings are for meetings and the afternoons are for deep work (or vice versa).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Meeting invites must include GAP: goals, an agenda, and pre-meeting preparation. If they don't, you can skip the meeting — no GAP, no need to attend.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If the company buys into this way of working, the ROI is potentially large. It's a mindset shift. But for me personally, my best work is done when I lose all track of time, forget to eat, and come out on the other side with shit fucking done. Imagine if an entire company could optimize their days to get as much of that time as possible. Your competition is in trouble.&lt;/p&gt;

</description>
      <category>deepwork</category>
      <category>flow</category>
      <category>productivity</category>
      <category>calnewport</category>
    </item>
    <item>
      <title>Death to (Synchronous) Standups</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Tue, 04 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/death-to-synchronous-standups-e0m</link>
      <guid>https://dev.to/therockstorm/death-to-synchronous-standups-e0m</guid>
      <description>&lt;p&gt;Some back of the napkin math no one asked for about how much daily standups cost your team compared to asynchronous standup tools like &lt;a href="https://geekbot.com/"&gt;Geekbot&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assuming 20 mins per standup (this could be an understatement) and five mins for Geekbot (usually an overstatement), we'll say async standups save 15 mins/day per person.&lt;/li&gt;
&lt;li&gt;Assuming a standup including ten people, that's 150 mins/day or 2.5 hrs/day, 12.5 hrs/wk, and ~625 hrs/yr (assuming 50 weeks worked).&lt;/li&gt;
&lt;li&gt;625 hrs/yr is over 15.625 weeks(!) of time per year spent in standup or nearly eight full sprints. So just under a sprint per person per year spent in this single meeting.&lt;/li&gt;
&lt;li&gt;Assuming a conservative average of $50/hr, that's over $30,000/yr. But this number pails in comparison to the opportunity cost of an extra sprint each year you could instead build products for customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Especially with more workers going remote, a common argument is that the meeting is more about human interaction. But I'd argue a dedicated, weekly meeting to discuss team member's weekends, etc. along with regularly scheduled happy hours work better. These can lead to deeper conversations and more positivity than hearing each other's voices each morning give status updates.&lt;/p&gt;

&lt;p&gt;In addition to the time savings, Slack-style standups give these added benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A historical record of what everyone said they were going to do and what they actually did.&lt;/li&gt;
&lt;li&gt;The productivity benefits of taking time to plan your day and get it in writing, helping to 1) stay accountable to yourself and others and 2) reduce distracting rabbit holes.&lt;/li&gt;
&lt;li&gt;Avoid the context switch (which studies have shown adds 15 mins and double my numbers above) of a meeting each morning and instead do your standup report when you're not in the middle of focused time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's up to each team to decide if an async standup is right for them. Hopefully, these numbers show the expense of daily standups. Make sure the benefits are outweighing these costs.&lt;/p&gt;

</description>
      <category>standups</category>
      <category>agile</category>
      <category>process</category>
      <category>meetings</category>
    </item>
    <item>
      <title>OpenAPI Spec-First API Development</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Wed, 07 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/openapi-spec-first-api-development-38ba</link>
      <guid>https://dev.to/therockstorm/openapi-spec-first-api-development-38ba</guid>
      <description>&lt;p&gt;Recent APIs I've built follow both the &lt;a href="https://swagger.io/specification/" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt; (formerly known as Swagger) and &lt;a href="https://jsonapi.org/" rel="noopener noreferrer"&gt;JSON:API&lt;/a&gt; specs. The former allows us to generate server-side data models and API clients in a number of languages using the &lt;a href="https://github.com/OpenAPITools/openapi-generator" rel="noopener noreferrer"&gt;OpenAPI Generator&lt;/a&gt;. The latter allows us to leverage shared conventions on how a RESTful JSON API should function.&lt;/p&gt;

&lt;p&gt;Through much trial and error, we've landed on a solid approach to automating much of our workflow when adding new API endpoints. The process begins as follows,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update the OpenAPI schema&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://vertx.io/docs/vertx-web-api-contract/java/" rel="noopener noreferrer"&gt;Vert.x Web API Contract&lt;/a&gt; to import the schema to handle things like authentication and request validation&lt;/li&gt;
&lt;li&gt;Use the OpenAPI generator to create server-side domain models and implement the endpoint&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since we offer &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; collections for our API, we chose to host our documentation using Postman as well. Ideally, we'd upload the new schema during our CI process, which would trigger the collection and documentation to update. Unfortunately, Postman wasn't built with this workflow in mind.&lt;/p&gt;

&lt;p&gt;They allow you to upload an OpenAPI schema and generate a collection from it, but not through their API. This means we can't automate it. So as a workaround, we use the &lt;a href="https://github.com/postmanlabs/openapi-to-postman" rel="noopener noreferrer"&gt;&lt;code&gt;openapi-to-postman&lt;/code&gt;&lt;/a&gt; tool to convert our schema to a Postman collection and then use their API to update the collection we provide our customers.&lt;/p&gt;

&lt;p&gt;A big problem with this tool, however, is that it generates new IDs for endpoints each time you run it. This makes diffs hard to follow and, more importantly, breaks documentation URLs. To workaround that, we use a &lt;a href="https://github.com/therockstorm/openapi-to-postman" rel="noopener noreferrer"&gt;forked version&lt;/a&gt; that stores a sourcemap of the endpoints so the IDs remain stable.&lt;/p&gt;

&lt;p&gt;From there, we call &lt;a href="https://www.postman.com/postman/workspace/postman-public-workspace/request/12959542-f9544c35-d4e1-4013-b2c2-88425c1cbcd2" rel="noopener noreferrer"&gt;Update Schema&lt;/a&gt; with the updated OpenAPI schema. This doesn't update Postman collections created from the schema, however, so we then call &lt;a href="https://www.postman.com/postman/workspace/postman-public-workspace/request/12959542-bc8b292b-ffbb-4c67-a2a1-2fc416e2aef8" rel="noopener noreferrer"&gt;Update Collection&lt;/a&gt;, which, thankfully, triggers the documentation to update.&lt;/p&gt;

&lt;p&gt;Now that our Postman collection, documentation, and code samples are updated, we use the OpenAPI generator to update our API clients and we're done.&lt;/p&gt;

&lt;p&gt;This seems like a relatively normal use-case: update the OpenAPI spec and have all downstream artifacts ("Golden" Collection, Documentation, Forked Collections, API clients) automatically update to reflect those changes. If you'd like to see Postman handle this better, add your thoughts to &lt;a href="https://github.com/postmanlabs/postman-app-support/issues/8940" rel="noopener noreferrer"&gt;this pull request&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>swagger</category>
      <category>rest</category>
      <category>postman</category>
    </item>
    <item>
      <title>Rust: First Impressions</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Thu, 04 Mar 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/rust-first-impressions-8c6</link>
      <guid>https://dev.to/therockstorm/rust-first-impressions-8c6</guid>
      <description>&lt;p&gt;Scala is great for recent microservices I've written, enabling incredible velocity. However, certain scenarios benefit from lower-level languages.&lt;/p&gt;

&lt;p&gt;I have five years of (dated) C++ experience. It's a high learning curve and even experts can cause segfaults, undefined behavior, double free errors, array overruns, mishandle concurrency, etc. This prevents novices from jumping into code for fear of breaking things, slowing productivity. Even modern C++ lacks a strong tooling ecosystem, making some things, including AWS Lambdas, a "batteries not included" experience.&lt;/p&gt;

&lt;p&gt;For these reasons, I spent time looking at how Rust and Go compare. The key requirements,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrates with existing code, libraries, and tools&lt;/li&gt;
&lt;li&gt;Increases productivity&lt;/li&gt;
&lt;li&gt;Reduces bugs by catching preventable ones early and being hard to misuse&lt;/li&gt;
&lt;li&gt;Negligible performance differences&lt;/li&gt;
&lt;li&gt;Comparable learning curve (ideally, shorter)&lt;/li&gt;
&lt;li&gt;Doesn't impact recruiting (ideally, improves)&lt;/li&gt;
&lt;li&gt;Stable with backward compatibility, meets future needs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both Rust and Go are great, but have different priorities as &lt;a href="https://bitfieldconsulting.com/golang/rust-vs-go" rel="noopener noreferrer"&gt;this article describes&lt;/a&gt;. Compared to Go, Rust has better C++ interoperability, no garbage collector (leading to better and more consistent performance), and richer language features (at the cost of additional complexity/higher learning curve). Taken together, these fit my use cases and preferences better.&lt;/p&gt;

&lt;p&gt;Addressing the "Key Requirements" above, Rust has&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Great C++ interoperability via the &lt;a href="https://github.com/dtolnay/cxx" rel="noopener noreferrer"&gt;CXX crate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;an aim to be &lt;a href="https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust.html" rel="noopener noreferrer"&gt;faster than C&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Excellent tooling

&lt;ul&gt;
&lt;li&gt;Cargo provides compile, test, format, lint, package management, and workspace/monorepo support&lt;/li&gt;
&lt;li&gt;VS Code extensions provide the above along with in-editor line coverage&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A sophisticated compiler giving non-experts the confidence to make changes since it won't - build code with mistakes easy to make in C++&lt;/li&gt;

&lt;li&gt;Concurrency via familiar &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;Zero-cost abstractions around things like generics&lt;/li&gt;

&lt;li&gt;Functional programming features like iterators and closures&lt;/li&gt;

&lt;li&gt;A devoted following as Stack Overflow's &lt;a href="https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-languages-loved" rel="noopener noreferrer"&gt;Most Loved Language&lt;/a&gt; five years running&lt;/li&gt;

&lt;li&gt;A bright future. As our front-end apps grow in complexity, Rust's relative approachability and WASM compilation may become important. Other languages have this capability, but at least at this point garbage collection adds client-side baggage.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Additional resources include,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fasterthanli.me/articles/a-half-hour-to-learn-rust" rel="noopener noreferrer"&gt;A half-hour to learn Rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doc.rust-lang.org/book/" rel="noopener noreferrer"&gt;The Rust Programming Language&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rust-lang.github.io/async-book/" rel="noopener noreferrer"&gt;Asynchronous Programming in Rust&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>firstimpressions</category>
      <category>cargo</category>
      <category>learning</category>
    </item>
    <item>
      <title>Running Linux GUI Apps in Docker on Mac</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Tue, 02 Feb 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/running-linux-gui-apps-in-docker-on-mac-d79</link>
      <guid>https://dev.to/therockstorm/running-linux-gui-apps-in-docker-on-mac-d79</guid>
      <description>&lt;p&gt;Why would you want to run Linux apps in Docker on a Mac? Maybe there's a Linux program you love or you want the added security of sandboxing an app. Or maybe, as was the case with me, you're developing a C++ program targeting Linux and don't want to cross compile your dependencies.&lt;/p&gt;

&lt;p&gt;The following steps get the CLion IDE running in a Docker container created during the project's build process. It uses &lt;a href="https://www.xquartz.org/"&gt;XQuartz&lt;/a&gt;, a version of the X Window System that runs on macOS, along with &lt;a href="https://linux.die.net/man/1/socat"&gt;socat&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the Linux version of CLion&lt;/li&gt;
&lt;li&gt;Each time dependencies change, run &lt;code&gt;create_dev_container.sh&lt;/code&gt; to create a Docker image and container. The script assumes &lt;code&gt;~/dev&lt;/code&gt; is your code directory. If you use something different, edit the script or run it as &lt;code&gt;CODE_DIR=&amp;lt;your_dir&amp;gt; ./create_dev_container.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;start_dev_container.sh&lt;/code&gt; to start the container and exec bash inside of it&lt;/li&gt;
&lt;li&gt;From inside the container, run &lt;code&gt;/home/dev/clion-XXX/bin/clion.sh&lt;/code&gt; to start CLion. If this fails, ensure your container contains a JRE by either manually running &lt;code&gt;apt install default-jre&lt;/code&gt; or adding it to the Dockerfile.&lt;/li&gt;
&lt;li&gt;Either select "Evaluate for free" or enter your activation code&lt;/li&gt;
&lt;li&gt;Open your project&lt;/li&gt;
&lt;li&gt;Build it and enjoy code completion, etc.!&lt;/li&gt;
&lt;li&gt;If you make changes to settings they'll be lost each time you create a new container. To save them, run &lt;code&gt;docker cp my_project:/root/.CLionXXX/ ~/dev&lt;/code&gt; and import them next time you start CLion in a fresh container.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;create\_dev\_container.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my_project
&lt;span class="nv"&gt;EXISTING_IMAGE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"name=&lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.Image}}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXISTING_IMAGE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt; already exists. Delete it? "&lt;/span&gt; answer

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true
  &lt;/span&gt;&lt;span class="k"&gt;do
    case&lt;/span&gt; &lt;span class="nv"&gt;$answer&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;yY]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; docker stop &lt;span class="nv"&gt;$NAME&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
            docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$NAME&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
            docker rmi &lt;span class="nv"&gt;$EXISTING_IMAGE_ID&lt;/span&gt;
            &lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"exiting"&lt;/span&gt;
            &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
  &lt;span class="k"&gt;done
else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt; does not exist, creating..."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Build your docker container&lt;/span&gt;
./build.sh

&lt;span class="nv"&gt;CODE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CODE_DIR&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/dev/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;IP_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ifconfig en0 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"inet "&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.ID}}"&lt;/span&gt; build/local/my-project:latest&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating container using image ID &lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_ID&lt;/span&gt;&lt;span class="s2"&gt;, mapping IP address &lt;/span&gt;&lt;span class="nv"&gt;$IP_ADDRESS&lt;/span&gt;&lt;span class="s2"&gt;, and mounting &lt;/span&gt;&lt;span class="nv"&gt;$CODE_DIR&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
docker create &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DISPLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$IP_ADDRESS&lt;/span&gt;:0 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;$CODE_DIR&lt;/span&gt;:/home/dev/ &lt;span class="nv"&gt;$IMAGE_ID&lt;/span&gt; bash

&lt;span class="nv"&gt;GENERATED_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"ancestor=&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.Names}}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Renaming &lt;/span&gt;&lt;span class="nv"&gt;$GENERATED_NAME&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
docker rename &lt;span class="nv"&gt;$GENERATED_NAME&lt;/span&gt; &lt;span class="nv"&gt;$NAME&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Container created, run ./start_dev_container.sh to exec bash inside of it."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;start\_dev\_container.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my_project

&lt;span class="nv"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;socat
brew &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--versions&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt;
&lt;span class="nv"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xquartz
brew cask &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--versions&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; brew cask &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsof &lt;span class="nt"&gt;-nP&lt;/span&gt; &lt;span class="nt"&gt;-i4TCP&lt;/span&gt;:6000 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; LISTEN&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0]&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$DISPLAY&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;open &lt;span class="nt"&gt;-a&lt;/span&gt; Xquartz
docker start &lt;span class="nv"&gt;$NAME&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nv"&gt;$NAME&lt;/span&gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>mac</category>
      <category>docker</category>
      <category>linux</category>
      <category>gui</category>
    </item>
    <item>
      <title>Start Local Services Fast in Zero Lines of Code with Docker Compose</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Mon, 04 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/start-local-services-fast-in-zero-lines-of-code-with-docker-compose-1do0</link>
      <guid>https://dev.to/therockstorm/start-local-services-fast-in-zero-lines-of-code-with-docker-compose-1do0</guid>
      <description>&lt;p&gt;...unless YAML counts as code.&lt;/p&gt;

&lt;p&gt;Many companies have home-grown scripts and tools to start services locally. In my experience, the scripts start as well-intentioned wrappers simplifying common &lt;code&gt;docker&lt;/code&gt; and &lt;code&gt;docker-compose&lt;/code&gt; commands. They tend to age poorly, however. They get complex, are a maintenance burden, and force developers to learn the wrapper commands instead of the underlying tool commands they may already be familiar with. They don't wrap each flag or option, making them less flexible. When the tools they wrap update, the tool must change to take advantage of new features. This, in turn, can lead to using deprecated, slow, or less secure commands, impacting the engineering team's ability to deliver value to customers.&lt;/p&gt;

&lt;p&gt;Twice I have converted internal tools to use &lt;code&gt;docker-compose&lt;/code&gt; directly. Using this approach enables,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://yaml.org/type/merge.html"&gt;YAML's merge syntax&lt;/a&gt; for sharing common configuration between services&lt;/li&gt;
&lt;li&gt;Showing all services and their dependencies in once place (or multiple, depending on your use-case)&lt;/li&gt;
&lt;li&gt;Quickly iterating on configuration changes&lt;/li&gt;
&lt;li&gt;Relying on robust, community-supported CLI with no maintenance requirements&lt;/li&gt;
&lt;li&gt;Using new features on day one instead having to add support&lt;/li&gt;
&lt;li&gt;Exporting the &lt;code&gt;COMPOSE_FILE&lt;/code&gt; variable so you can start services from any directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll walk through an example &lt;code&gt;docker-compose.yml&lt;/code&gt; file using this approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.4"&lt;/span&gt;

&lt;span class="na"&gt;x-depends-on-postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;depends-on-postgres&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;service_healthy&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

&lt;span class="s"&gt;x-interval5-retries45-timeout5&lt;/span&gt;
  &lt;span class="s"&gt;interval&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
  &lt;span class="s"&gt;retries&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, note we use v2.4 syntax instead of v3.x. Think of &lt;code&gt;docker-compose&lt;/code&gt; v2 and v3 more like a fork than an upgrade, with v2 perfectly capable provided you don't need Docker Swarm support. Ideally, your services are resilient to dependencies being down, allowing you to start every service in parallel. If they aren't, &lt;code&gt;docker-compose&lt;/code&gt; v2.4 syntax allows using &lt;code&gt;depends_on&lt;/code&gt; &lt;code&gt;condition&lt;/code&gt;s and &lt;code&gt;healthcheck&lt;/code&gt; keys together, allowing you to start services in parallel if they're not dependent on each other and in order if they are. If you're using v3.x, &lt;a href="https://docs.docker.com/compose/startup-order/"&gt;this page&lt;/a&gt; gives options to accomplish something similar, they're just more work and, sadly, there's no plan to add the v2 functionality back.&lt;/p&gt;

&lt;p&gt;Next, we add common configuration using keys that start with &lt;code&gt;x-&lt;/code&gt; via &lt;a href="https://yaml.org/type/merge.html"&gt;YAML merge key types&lt;/a&gt;, which &lt;a href="https://docs.docker.com/compose/compose-file/compose-file-v2/#extension-fields"&gt;&lt;code&gt;docker-compose&lt;/code&gt; supports&lt;/a&gt;. This example only has two services so not much is shared, but this can lead to a large reduction in duplication for more realistic scenarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;my-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*depends-on-postgres&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-service&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MY_SERVICE_REPO:-MY_DEFAULT_REGISTRY}/my-service:latest&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PGDATA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data/pgdata&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-service-data&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xxx&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;res=$$(echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'SELECT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;psql&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--host&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--username&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$$POSTGRES_USER&lt;/span&gt;
        &lt;span class="s"&gt;--dbname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$$POSTGRES_DATABASE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--quiet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--no-align&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--tuples-only)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="s"&gt;[$$res&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'1']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*interval5-retries45-timeout5&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:12&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;5432&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;5432&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data/postgres:/var/lib/postgresql/data/pgdata:delegated&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;services&lt;/code&gt; section, add each as you normally would. Then, specify the &lt;code&gt;depends_on&lt;/code&gt; key for any service with dependencies and the &lt;code&gt;healthcheck&lt;/code&gt; key for any service depended on by another service. To add a healthcheck,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many services may share a healthcheck, in that case, pull it into a YAML merge key type to reduce duplication&lt;/li&gt;
&lt;li&gt;For new or one-off services, start by &lt;a href="https://github.com/docker-library/healthcheck"&gt;looking here&lt;/a&gt; to see if one exists or for inspiration. Once you have a command you think works, &lt;code&gt;exec&lt;/code&gt; into the container with &lt;code&gt;docker-compose exec SERVICE sh&lt;/code&gt; and test it. Some containers won't have &lt;code&gt;curl&lt;/code&gt; or other tools, so you may need to get creative. Once it's working, add the command to &lt;code&gt;docker-compose.yml&lt;/code&gt;, start the service, and run &lt;code&gt;docker-compose ps&lt;/code&gt; to ensure it becomes healthy. If it doesn't, run &lt;code&gt;docker inspect $(docker-compose ps -q SERVICE) | jq '.[].State.Health'&lt;/code&gt; to see what's being returned, edit the command, and try again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll notice the image for &lt;code&gt;my-service&lt;/code&gt;'s image is &lt;code&gt;${MY_SERVICE_REPO:-MY_DEFAULT_REGISTRY}/my-service:latest&lt;/code&gt;. This allows quickly swapping between local and hosted images &lt;a href="https://docs.docker.com/compose/environment-variables/"&gt;using a &lt;code&gt;.env&lt;/code&gt; file&lt;/a&gt;. By default, it uses &lt;code&gt;MY_DEFAULT_REGISTRY&lt;/code&gt;. To use a local image, add &lt;code&gt;MY_SERVICE_REPO=local&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ...as shown above&lt;/span&gt;

  &lt;span class="na"&gt;flow-backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;my-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;service_healthy&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;# ...other services&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rwgrim/docker-noop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll likely have services you tend to start together. To make this easier, optionally add "flows" at the bottom of &lt;code&gt;services&lt;/code&gt;. These only exist to start their dependents, so they use the tiny &lt;a href="https://hub.docker.com/r/rwgrim/docker-noop"&gt;&lt;code&gt;rwgrim/docker-noop&lt;/code&gt;&lt;/a&gt; image, which simply exits successfully. In a real application, maybe you'll have &lt;code&gt;flow-backend&lt;/code&gt;, &lt;code&gt;flow-frontend&lt;/code&gt;, and then a &lt;code&gt;flow-all&lt;/code&gt; that depends on both of the previous flows.&lt;/p&gt;

&lt;p&gt;Here's the full example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.4"&lt;/span&gt; &lt;span class="c1"&gt;# v3 doesn't support using depends_on conditions with healthcheck&lt;/span&gt;

&lt;span class="c1"&gt;#############################################################################&lt;/span&gt;
&lt;span class="c1"&gt;# Shared config via merge key types, https://yaml.org/type/merge.html&lt;/span&gt;
&lt;span class="c1"&gt;#############################################################################&lt;/span&gt;
&lt;span class="na"&gt;x-depends-on-postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;depends-on-postgres&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;service_healthy&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

&lt;span class="s"&gt;x-interval5-retries45-timeout5&lt;/span&gt;
  &lt;span class="s"&gt;interval&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
  &lt;span class="s"&gt;retries&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;#############################################################################&lt;/span&gt;
  &lt;span class="c1"&gt;# Services&lt;/span&gt;
  &lt;span class="c1"&gt;#############################################################################&lt;/span&gt;
  &lt;span class="na"&gt;my-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*depends-on-postgres&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-service&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MY_SERVICE_REPO:-MY_DEFAULT_REGISTRY}/my-service:latest&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PGDATA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data/pgdata&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-service-data&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xxx&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;res=$$(echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'SELECT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;psql&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--host&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--username&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$$POSTGRES_USER&lt;/span&gt;
        &lt;span class="s"&gt;--dbname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$$POSTGRES_DATABASE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--quiet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--no-align&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--tuples-only)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="s"&gt;[$$res&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'1']&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*interval5-retries45-timeout5&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:12&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;5432&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;5432&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data/postgres:/var/lib/postgresql/data/pgdata:delegated&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

  &lt;span class="c1"&gt;#############################################################################&lt;/span&gt;
  &lt;span class="c1"&gt;# Flows&lt;/span&gt;
  &lt;span class="c1"&gt;#############################################################################&lt;/span&gt;
  &lt;span class="na"&gt;flow-backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;my-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;service_healthy&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;# ...other services&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rwgrim/docker-noop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For even higher productivity, export &lt;code&gt;COMPOSE_FILE='path/to/docker-compose.yml'&lt;/code&gt; and create the alias &lt;code&gt;alias dcp=docker-compose&lt;/code&gt;, allowing you to start services from any directory!&lt;/p&gt;

&lt;p&gt;Day-to-day, you'll use &lt;code&gt;docker-compose&lt;/code&gt; commands directly. Common commands: run &lt;code&gt;dcp up -d SERVICE_1 SERVICE_2 ...&lt;/code&gt; to create and start the provided services and their dependencies (the &lt;code&gt;-d&lt;/code&gt; or &lt;code&gt;--detach&lt;/code&gt; flag runs them in the background), &lt;code&gt;dcp ps&lt;/code&gt; to list their statuses, &lt;code&gt;dcp stop&lt;/code&gt; to stop them, &lt;code&gt;dcp start&lt;/code&gt; to start them back up, and &lt;code&gt;dcp down&lt;/code&gt; to remove containers, images, and volumes. Run &lt;code&gt;dcp pull&lt;/code&gt; to download the latest images, &lt;code&gt;dcp logs SERVICE_1 SERVICE_2&lt;/code&gt; to view logs, &lt;code&gt;dcp config --services&lt;/code&gt; to validate the &lt;code&gt;docker-compose.yml&lt;/code&gt; file and list all services defined, and &lt;code&gt;dcp config --services | grep flow&lt;/code&gt; to list only flows. Run &lt;code&gt;dcp -h&lt;/code&gt; for all commands.&lt;/p&gt;

&lt;p&gt;And that's it! All services configured in one file with minimal duplication that you can run anywhere using the newest features of a community-supported CLI with no code to maintain!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>dockercompose</category>
      <category>local</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Git Fundamentals</title>
      <dc:creator>Rocky Warren</dc:creator>
      <pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/therockstorm/git-fundamentals-mlp</link>
      <guid>https://dev.to/therockstorm/git-fundamentals-mlp</guid>
      <description>&lt;p&gt;Git can seem overwhelming, but while performing typical day-to-day tasks, you'll use only about ten commands. If you happen to use &lt;a href="https://ohmyz.sh/"&gt;Oh My ZSH&lt;/a&gt;, the included &lt;a href="https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git"&gt;&lt;code&gt;git&lt;/code&gt; plugin&lt;/a&gt; adds many &lt;a href="https://www.rocky.dev/blog/linux-bash-aliases"&gt;aliases&lt;/a&gt;. Below each command, I've included the corresponding alias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start a new branch for a new feature&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; new-feature
gcb new-feature

&lt;span class="c"&gt;# Commit as you make changes (the --all adds changed files to&lt;/span&gt;
&lt;span class="c"&gt;# `staging`)&lt;/span&gt;
git commit &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"your commit message"&lt;/span&gt;
gcam &lt;span class="s2"&gt;"your commit message"&lt;/span&gt;

&lt;span class="c"&gt;# Add new or deleted files to `staging` (the `git commit&lt;/span&gt;
&lt;span class="c"&gt;# --all` above only adds tracked files)&lt;/span&gt;
git add &lt;span class="nt"&gt;--all&lt;/span&gt;
gaa

&lt;span class="c"&gt;# Check the status of your branch/commits&lt;/span&gt;
git status
gst

&lt;span class="c"&gt;# Switch branches&lt;/span&gt;
git checkout branch-name
gco branch-name
&lt;span class="c"&gt;# Separate `git` plugin alias to switch back to master&lt;/span&gt;
gcm

&lt;span class="c"&gt;# See what has changed (maybe done prior to committing, include&lt;/span&gt;
&lt;span class="c"&gt;# the `--cached` flag to see what has changed in `staging`)&lt;/span&gt;
git diff
gd

&lt;span class="c"&gt;# Pull new changes down from remote (Github/GitLab/etc.)&lt;/span&gt;
git pull
gl

&lt;span class="c"&gt;# Push changes to remote&lt;/span&gt;
git push &lt;span class="nt"&gt;--set-upstream&lt;/span&gt; origin
gpsup

&lt;span class="c"&gt;# Merge changes from another branch into the current one&lt;/span&gt;
git merge other-branch-name
gm other-branch-name

&lt;span class="c"&gt;# Or, if you prefer rebase,&lt;/span&gt;
git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; other-branch-name
grbi other-branch-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! 95% of the time, you'll use these commands. Google was invented for the remaining 5% 😉&lt;/p&gt;

</description>
      <category>git</category>
      <category>fundamentals</category>
      <category>learning</category>
      <category>bash</category>
    </item>
  </channel>
</rss>
