<?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: Kolide</title>
    <description>The latest articles on DEV Community by Kolide (@kolide).</description>
    <link>https://dev.to/kolide</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%2Forganization%2Fprofile_image%2F1185%2F22eab2db-34da-4706-902e-a2ecdece2006.png</url>
      <title>DEV Community: Kolide</title>
      <link>https://dev.to/kolide</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kolide"/>
    <language>en</language>
    <item>
      <title>A Rails Multi-Tenant Strategy That's ~30 Lines and "Just Works"</title>
      <dc:creator>Jason Meller</dc:creator>
      <pubDate>Tue, 09 Nov 2021 15:33:14 +0000</pubDate>
      <link>https://dev.to/kolide/a-rails-multi-tenant-strategy-thats-30-lines-and-just-works-58cd</link>
      <guid>https://dev.to/kolide/a-rails-multi-tenant-strategy-thats-30-lines-and-just-works-58cd</guid>
      <description>&lt;p&gt;When engineering a new SaaS app, how you plan to handle customer data tenancy is usually one of the first decisions you and your team will need to make. If you are writing a Rails app and decide on a multi-tenant strategy (one instance of the app serves many customers), then this article is for you.&lt;/p&gt;

&lt;p&gt;I contend that modern Rails has everything you need to build a multi-tenant strategy that scales, is easy for others to use, and can be written in just a handful of lines of code. Kolide (&lt;a href="https://angel.co/company/kolideco/jobs/1638302-rails-engineer" rel="noopener noreferrer"&gt;btw we're hiring&lt;/a&gt;) has been using this simple strategy since the inception of its product, and it's been one of the most elegant and easiest to understand parts of our code-base. &lt;/p&gt;

&lt;p&gt;So before reaching for a gem, consider if the following meets your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;p&gt;The entire implementation is contained in just two files and requires no additional dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/concerns/account_ownable.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;AccountOwnable&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Account is actually not optional, but we not do want&lt;/span&gt;
    &lt;span class="c1"&gt;# to generate a SELECT query to verify the account is&lt;/span&gt;
    &lt;span class="c1"&gt;# there every time. We get this protection for free&lt;/span&gt;
    &lt;span class="c1"&gt;# because of the `Current.account_or_raise!`     &lt;/span&gt;
    &lt;span class="c1"&gt;# and also through FK constraints.&lt;/span&gt;
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;optional: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;default_scope&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;account: &lt;/span&gt;&lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account_or_raise!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/current.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Current&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CurrentAttributes&lt;/span&gt;
  &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;

  &lt;span class="n"&gt;resets&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MissingCurrentAccount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;account_or_raise!&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MissingCurrentAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"You must set an account with Current.account="&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;

    &lt;span class="n"&gt;account&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;
    &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zone&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time_zone&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using the Code In Practice
&lt;/h3&gt;

&lt;p&gt;To use this code, simply mix-in the concern into any standard ActiveRecord model like so...&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiKey&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="c1"&gt;# assumes table has a column named `account_id`&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;AccountOwnable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When a user of ours signs in, all we need to do is simply set &lt;code&gt;Current.user&lt;/code&gt; in our authentication controller concern which is mixed into our &lt;code&gt;ApplicationController&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/concerns/require_authentication.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;RequireAuthentication&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:ensure_authenticated_user&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ensure_authenticated_user&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_valid_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;signin_path&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For this small amount of effort we now get the following benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Because of the &lt;code&gt;default_scope&lt;/code&gt;, once a user is signed in, data from sensitive models is automatically scoped to their account. We just don't need to think about it, no matter how complicated our query chaining gets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Again, because of the &lt;code&gt;default_scope&lt;/code&gt; creating new records for these &lt;code&gt;AccountOwnable&lt;/code&gt; models will automatically set the &lt;code&gt;account_id&lt;/code&gt; for us. One less thing to think about.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In situations where we are outside of the standard Rails request/response paradigm (ex: in an ActiveJob) any &lt;code&gt;AccountOwnable&lt;/code&gt; models will raise if &lt;code&gt;Current.account&lt;/code&gt; is not set. This &lt;em&gt;forces&lt;/em&gt; us to constantly think about how we are scoping data for customer needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The situations where we need to enumerate through more than one tenant's data at a time are still possible but now require a &lt;code&gt;Model.unscoped&lt;/code&gt; which can be easily scanned for in linters requiring engineers to justify their rationale on a per use-case basis.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One thing that became slightly annoying was constantly setting &lt;code&gt;Current.account =&lt;/code&gt; in the Rails console. To make that much easier we wrote a simple console command.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/kolide/console.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;App&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Console&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Current account switched to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# in config/application.rb&lt;/span&gt;
&lt;span class="n"&gt;console&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'kolide/console'&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ConsoleMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="ss"&gt;:include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Kolide&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Console&lt;/span&gt;
  &lt;span class="no"&gt;TOPLEVEL_BINDING&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'self'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Kolide&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Console&lt;/span&gt; &lt;span class="c1"&gt;# PRY&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we simply run &lt;code&gt;t 1&lt;/code&gt; when we want to switch the tenant with an id of 1. Much better.&lt;/p&gt;

&lt;p&gt;In the test suite, you should also reset &lt;code&gt;Current&lt;/code&gt; before each spec/test as it's not done for you automatically. For us that was simply a matter of adding...&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spec/spec_helper.rb&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we don't have to worry about our global state being polluted when running our specs serially in the same process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Concerns we had that didn't end up being an issue
&lt;/h3&gt;

&lt;p&gt;Kolide has been successfully using this strategy since the inception of our Ruby on Rails SaaS app. While we arrived at this strategy in the first few days of our app's formation, we definitely were less confident in the approach. Here is a list of concerns we held and how they ended up panning out.&lt;/p&gt;
&lt;h4&gt;
  
  
  Will this approach be acceptable to our customers?
&lt;/h4&gt;

&lt;p&gt;Kolide is a device security company, and since our buyers are likely to be either security engineers or security minded IT staff, the bar we need to meet is much higher than the normal SaaS company. We were nervous that an app-enforced constraint would &lt;em&gt;feel&lt;/em&gt; flimsy, despite how well it works in practice.&lt;/p&gt;

&lt;p&gt;In reality, we found the opposite. Customers were mostly ambivalent about our app-enforced constraint approach. Why? It's because it's an approach that's common among their other vendors and matches their pre-conceived expectations about how most SaaS software works. Matched expectations = less concern.&lt;/p&gt;

&lt;p&gt;In prior iterations of our app where we did extreme things like spin up separate Kubernetes namespaces and DBs for each customer, we found our efforts were paradoxically met with more concern, not less.  This concern manifested as additional process, review, and ultimately unnecessary friction as our buyers grappled to bring more and more technical folks into the procurement process to simply understand the unfamiliar architecture.&lt;/p&gt;

&lt;p&gt;With our current approach, our development and deployment story is simpler, and simplicity has significant security advantages.&lt;/p&gt;
&lt;h4&gt;
  
  
  Current.rb is too magical, will multi-threading in production cause someone's &lt;code&gt;default_scope&lt;/code&gt; to leak to another request?
&lt;/h4&gt;

&lt;p&gt;There is a lot of consternation in the Rails community when DHH introduced the &lt;code&gt;CurrentAttributes&lt;/code&gt; paradigm in Rails 5.2. DHH talks about his rationale for adding this in his Youtube video entitled, "Using globals when the price is right".&lt;/p&gt;

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

&lt;p&gt;Ryan Bigg on the other-hand felt this addition to Rails would cause developers to write a lot of code with unpredictable behavior expressed these views in his blog post entitled, &lt;a href="https://ryanbigg.com/2017/06/current-considered-harmful" rel="noopener noreferrer"&gt;"Rails' CurrentAttributes considered harmful"&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After reading more into the original PR...&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/rails/rails/pull/29180" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        ActiveSupport::CurrentAttributes provides a thread-isolated attributes singleton
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#29180&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/dhh" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F2741%3Fv%3D4" alt="dhh avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/dhh" rel="noopener noreferrer"&gt;dhh&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/rails/rails/pull/29180" rel="noopener noreferrer"&gt;&lt;time&gt;May 22, 2017&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Abstract super class that provides a thread-isolated attributes singleton.
Primary use case is keeping all the per-request attributes easily available to the whole system.&lt;/p&gt;
&lt;p&gt;The following full app-like example demonstrates how to use a Current class to
facilitate easy access to the global, per-request attributes without passing them deeply
around everywhere:&lt;/p&gt;
&lt;div class="highlight highlight-source-ruby js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;# app/models/current.rb&lt;/span&gt;
&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;Current&lt;/span&gt; &amp;lt; &lt;span class="pl-v"&gt;ActiveSupport&lt;/span&gt;::&lt;span class="pl-v"&gt;CurrentAttributes&lt;/span&gt;
  &lt;span class="pl-en"&gt;attribute&lt;/span&gt; &lt;span class="pl-pds"&gt;:account&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-pds"&gt;:user&lt;/span&gt;
  &lt;span class="pl-en"&gt;attribute&lt;/span&gt; &lt;span class="pl-pds"&gt;:request_id&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-pds"&gt;:user_agent&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-pds"&gt;:ip_address&lt;/span&gt; 
  
  &lt;span class="pl-en"&gt;resets&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-v"&gt;Time&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;zone&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;nil&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  
  &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;user&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;user&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
    &lt;span class="pl-smi"&gt;super&lt;/span&gt;
    &lt;span class="pl-smi"&gt;self&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;account&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;user&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;account&lt;/span&gt;
    &lt;span class="pl-v"&gt;Time&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;zone&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;user&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;time_zone&lt;/span&gt;
  &lt;span class="pl-k"&gt;end&lt;/span&gt;
&lt;span class="pl-k"&gt;end&lt;/span&gt;

&lt;span class="pl-c"&gt;# app/controllers/concerns/authentication.rb&lt;/span&gt;
&lt;span class="pl-k"&gt;module&lt;/span&gt; &lt;span class="pl-v"&gt;Authentication&lt;/span&gt;
  &lt;span class="pl-en"&gt;extend&lt;/span&gt; &lt;span class="pl-v"&gt;ActiveSupport&lt;/span&gt;::&lt;span class="pl-v"&gt;Concern&lt;/span&gt;

  &lt;span class="pl-en"&gt;included&lt;/span&gt; &lt;span class="pl-k"&gt;do&lt;/span&gt;
    &lt;span class="pl-en"&gt;before_action&lt;/span&gt; &lt;span class="pl-pds"&gt;:set_current_authenticated_user&lt;/span&gt;
  &lt;span class="pl-k"&gt;end&lt;/span&gt;

  &lt;span class="pl-k"&gt;private&lt;/span&gt;
    &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;set_current_authenticated_user&lt;/span&gt;
      &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;user&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;User&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;find&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-en"&gt;cookies&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;signed&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-pds"&gt;:user_id&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
    &lt;span class="pl-k"&gt;end&lt;/span&gt;
&lt;span class="pl-k"&gt;end&lt;/span&gt;

&lt;span class="pl-c"&gt;# app/controllers/concerns/set_current_request_details.rb&lt;/span&gt;
&lt;span class="pl-k"&gt;module&lt;/span&gt; &lt;span class="pl-v"&gt;SetCurrentRequestDetails&lt;/span&gt;
  &lt;span class="pl-en"&gt;extend&lt;/span&gt; &lt;span class="pl-v"&gt;ActiveSupport&lt;/span&gt;::&lt;span class="pl-v"&gt;Concern&lt;/span&gt;

  &lt;span class="pl-en"&gt;included&lt;/span&gt; &lt;span class="pl-k"&gt;do&lt;/span&gt;
    &lt;span class="pl-en"&gt;before_action&lt;/span&gt; &lt;span class="pl-k"&gt;do&lt;/span&gt;
      &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;request_id&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;request&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;uuid&lt;/span&gt;
      &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;user_agent&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;request&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;user_agent&lt;/span&gt;
      &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;ip_address&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;request&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;ip&lt;/span&gt;
    &lt;span class="pl-k"&gt;end&lt;/span&gt;
  &lt;span class="pl-k"&gt;end&lt;/span&gt;
&lt;span class="pl-k"&gt;end&lt;/span&gt;  

&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;ApplicationController&lt;/span&gt; &amp;lt; &lt;span class="pl-v"&gt;ActionController&lt;/span&gt;::&lt;span class="pl-v"&gt;Base&lt;/span&gt;
  &lt;span class="pl-en"&gt;include&lt;/span&gt; &lt;span class="pl-v"&gt;Authentication&lt;/span&gt;
  &lt;span class="pl-en"&gt;include&lt;/span&gt; &lt;span class="pl-v"&gt;SetCurrentRequestDetails&lt;/span&gt;
&lt;span class="pl-k"&gt;end&lt;/span&gt;

&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;MessagesController&lt;/span&gt; &amp;lt; &lt;span class="pl-v"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;create&lt;/span&gt;
    &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;account&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;messages&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;create&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-en"&gt;message_params&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
  &lt;span class="pl-k"&gt;end&lt;/span&gt;
&lt;span class="pl-k"&gt;end&lt;/span&gt;

&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;Message&lt;/span&gt; &amp;lt; &lt;span class="pl-v"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="pl-en"&gt;belongs_to&lt;/span&gt; &lt;span class="pl-pds"&gt;:creator&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-pds"&gt;default&lt;/span&gt;: &lt;span class="pl-c1"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;user&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  &lt;span class="pl-en"&gt;after_create&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; |&lt;span class="pl-s1"&gt;message&lt;/span&gt;| &lt;span class="pl-v"&gt;Event&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;create&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-pds"&gt;record&lt;/span&gt;: &lt;span class="pl-s1"&gt;message&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;
&lt;span class="pl-k"&gt;end&lt;/span&gt;

&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;Event&lt;/span&gt; &amp;lt; &lt;span class="pl-v"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="pl-en"&gt;before_create&lt;/span&gt; &lt;span class="pl-k"&gt;do&lt;/span&gt;
    &lt;span class="pl-smi"&gt;self&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;request_id&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;request_id&lt;/span&gt;
    &lt;span class="pl-smi"&gt;self&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;user_agent&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;user_agent&lt;/span&gt;
    &lt;span class="pl-smi"&gt;self&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;ip_address&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Current&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;ip_address&lt;/span&gt;
  &lt;span class="pl-k"&gt;end&lt;/span&gt;
&lt;span class="pl-k"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;A word of caution: It's easy to overdo a global singleton like Current and tangle your model as a result.
Current should only be used for a few, top-level globals, like account, user, and request details.
The attributes stuck in Current should be used by more or less all actions on all requests. If you start
sticking controller-specific attributes in there, you're going to create a mess.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rails/rails/pull/29180" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;I found a lot of thoughtful consideration for how to make this code truly thread-safe which convinced me to bet big on this approach. &lt;/p&gt;

&lt;p&gt;Now over two years later, I can say with confidence that in our app, which serves nearly 1,800 HTTP requests per second across hundreds of different tenants, this code works as described in a real production setting. &lt;/p&gt;

&lt;h4&gt;
  
  
  Will setting &lt;code&gt;Current.account&lt;/code&gt; in asynchronous jobs or in the test suite become tiresome or problematic?
&lt;/h4&gt;

&lt;p&gt;No, the ceremony here is worth it because it forces us to think carefully about how we are acting on our customer's data. Situations where we need to iterate through more than one customer's data are also trivial to achieve through an &lt;code&gt;#each&lt;/code&gt; block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ApiKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unscoped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;
  &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convert_to_new_format&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;We are now sharing this simplistic approach because it's worked so well for us at Kolide. I imagine other burgeoning Rails apps considering a multi-tenant strategy will appreciate being able to do this in their own codebase vs offload something so important to an external gem.&lt;/p&gt;

&lt;p&gt;We will continue to share our learnings as Kolide grows. In fact prepping for future scale is what I like best about this approach. By marking all of our records with a tenant identifier like &lt;code&gt;account_id&lt;/code&gt; we gain the future option of leveraging more sophisticated solutions at the PostgreSQL level &lt;a href="https://edgeguides.rubyonrails.org/active_record_multiple_databases.html" rel="noopener noreferrer"&gt;like multi-DB sharding&lt;/a&gt; or &lt;a href="https://www.citusdata.com/blog/2017/01/05/easily-scale-out-multi-tenant-apps/" rel="noopener noreferrer"&gt;even products like Citus&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;I hope you found this post useful. If you found any errors in this guide or suggestions to improve it, please reach out in the comments or hit me up on twitter &lt;a href="https://twitter.com/jmeller" rel="noopener noreferrer"&gt;@jmeller&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://angel.co/company/kolideco/jobs/1638302-rails-engineer" rel="noopener noreferrer"&gt;Oh, and we're hiring!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>saas</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Migrate a Rails 6 App From sass-rails to cssbundling-rails</title>
      <dc:creator>Jason Meller</dc:creator>
      <pubDate>Thu, 21 Oct 2021 02:59:07 +0000</pubDate>
      <link>https://dev.to/kolide/how-to-migrate-a-rails-6-app-from-sass-rails-to-cssbundling-rails-4l41</link>
      <guid>https://dev.to/kolide/how-to-migrate-a-rails-6-app-from-sass-rails-to-cssbundling-rails-4l41</guid>
      <description>&lt;p&gt;At Kolide (&lt;a href="https://angel.co/company/kolideco/jobs/1638302-rails-engineer" rel="noopener noreferrer"&gt;btw we're hiring&lt;/a&gt;), we move swiftly to adopt to new versions of Ruby, Rails, and other major dependencies within a few months of them becoming available. We are at our happiest when we get to use the latest language and framework features. Additionally, forging ahead to uncharted waters allows us to contribute back bug reports, PRs, and guides for other rubyists also interested on being on the bleeding edge.&lt;/p&gt;

&lt;p&gt;To that end, I wanted to share our recent experience with upgrading our production Rails 6.1 app from sprockets/sass-rails to the brand-new cssbundling-rails. If you are considering an eventual transition to Rails 7, this is a great first step in that direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;cssbundling-rails&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Or, more precisely, why move aways from &lt;code&gt;sass-rails&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When the webpacker gem was released as part of &lt;a href="https://weblog.rubyonrails.org/2017/2/23/Rails-5-1-beta1/" rel="noopener noreferrer"&gt;Rails 5.1 release in 2017&lt;/a&gt;, DHH made it clear that while webpacker &lt;em&gt;could&lt;/em&gt; be used to bundle CSS, he highly recommended to keep it simple and continue to use the Rails asset pipeline. &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-808349072734027776-654" src="https://platform.twitter.com/embed/Tweet.html?id=808349072734027776"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-808349072734027776-654');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=808349072734027776&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Heeding this advice, many Rails projects (like ours) continue to dutifully serve their SCSS files via sprockets and the &lt;code&gt;sass-rails&lt;/code&gt; gem, the same way it's been doing circa Rails 3.1 in 2011.&lt;/p&gt;

&lt;p&gt;This sprockets setup has always worked great, but lately some serious bit-rot has set in. Over the last few years, the &lt;a href="https://sass-lang.com" rel="noopener noreferrer"&gt;Sass Team&lt;/a&gt; has deprecated both its original ruby-based version of &lt;code&gt;sass&lt;/code&gt;, and more recently, the &lt;code&gt;libsass/sassc&lt;/code&gt; library in favor of &lt;a href="https://sass-lang.com/dart-sass" rel="noopener noreferrer"&gt;dart-sass&lt;/a&gt;. As of this writing, I could not find any sprockets compatible versions of &lt;code&gt;dart-sass&lt;/code&gt;. Further, as time marches on, the sassc gem is beginning to accumulate some pretty &lt;a href="https://github.com/sass/sassc-ruby/issues/197" rel="noopener noreferrer"&gt;nasty bugs&lt;/a&gt; and &lt;a href="https://github.com/sass/sassc-ruby/issues/200" rel="noopener noreferrer"&gt;inefficiencies&lt;/a&gt;. With no fixes on the horizon, it's time to move on.&lt;/p&gt;

&lt;p&gt;This is where the newly minted &lt;a href="https://github.com/rails/cssbundling-rails" rel="noopener noreferrer"&gt;&lt;code&gt;cssbundling-rails&lt;/code&gt;&lt;/a&gt; comes in. Inspired by the also new &lt;a href="https://github.com/rails/jsbundling-rails" rel="noopener noreferrer"&gt;&lt;code&gt;jsbundling-rails&lt;/code&gt;&lt;/a&gt; library, it allows folks to leverage &lt;code&gt;yarn/npm&lt;/code&gt; to build a much simpler and more canonical CSS processing pipeline with a setup that will be familiar to JS developers.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rails" rel="noopener noreferrer"&gt;
        rails
      &lt;/a&gt; / &lt;a href="https://github.com/rails/cssbundling-rails" rel="noopener noreferrer"&gt;
        cssbundling-rails
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Bundle and process CSS in Rails with Tailwind, PostCSS, and Sass via Node.js.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;CSS Bundling for Rails&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Use &lt;a href="https://tailwindcss.com" rel="nofollow noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt;, &lt;a href="https://getbootstrap.com/" rel="nofollow noopener noreferrer"&gt;Bootstrap&lt;/a&gt;, &lt;a href="https://bulma.io/" rel="nofollow noopener noreferrer"&gt;Bulma&lt;/a&gt;, &lt;a href="https://postcss.org" rel="nofollow noopener noreferrer"&gt;PostCSS&lt;/a&gt;, or &lt;a href="https://sass-lang.com/" rel="nofollow noopener noreferrer"&gt;Dart Sass&lt;/a&gt; to bundle and process your CSS, then deliver it via the asset pipeline in Rails. This gem provides installers to get you going with the bundler of your choice in a new Rails application, and a convention to use &lt;code&gt;app/assets/builds&lt;/code&gt; to hold your bundled output as artifacts that are not checked into source control (the installer adds this directory to &lt;code&gt;.gitignore&lt;/code&gt; by default).&lt;/p&gt;
&lt;p&gt;You develop using this approach by running the bundler in watch mode in a terminal with &lt;code&gt;yarn build:css --watch&lt;/code&gt; (and your Rails server in another, if you're not using something like &lt;a href="https://github.com/puma/puma-dev" rel="noopener noreferrer"&gt;puma-dev&lt;/a&gt;). You can also use &lt;code&gt;./bin/dev&lt;/code&gt;, which will start both the Rails server and the CSS build watcher (along with a JS build watcher, if you're also using &lt;code&gt;jsbundling-rails&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Whenever the bundler detects changes to any…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rails/cssbundling-rails" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;With our mission set, let's roll up our sleeves and get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading a Rails 6.x App
&lt;/h2&gt;

&lt;p&gt;According to its gemspec, &lt;code&gt;cssbundling-rails&lt;/code&gt; is not just for new Rails 7 apps, it's also compatible with 6.0.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Prepare Your &lt;code&gt;Gemfile&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Our goal is to not just transition to &lt;code&gt;cssbundling-rails&lt;/code&gt;, but to also remove &lt;code&gt;sass-rails&lt;/code&gt; gem.  To get started, remove &lt;code&gt;sass-rails&lt;/code&gt; and any other potential references to sass like &lt;code&gt;sass-ruby&lt;/code&gt; and &lt;code&gt;sassc&lt;/code&gt; (if defined).&lt;/p&gt;

&lt;p&gt;Next, add &lt;code&gt;gem cssbundling-rails, '&amp;gt;= 0.2.4'&lt;/code&gt; (the version at the time of this writing) and run &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Prepare Your SCSS Files
&lt;/h3&gt;

&lt;p&gt;First, let's take some inventory. Open up &lt;code&gt;config/initializers/assets.rb&lt;/code&gt; and at the bottom of that file you will see something like the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# Precompile additional assets.&lt;/span&gt;
&lt;span class="c1"&gt;# application.js, application.css, and all non-JS/CSS in the app/assets&lt;/span&gt;
&lt;span class="c1"&gt;# folder are already added.&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;precompile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sx"&gt;%w( sessions.css staff.css marketing.css )&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If the &lt;code&gt;Rails.application.config.assets.precompile&lt;/code&gt; line is uncommented, take note of the &lt;code&gt;.css&lt;/code&gt; files referenced in the array. In addition to &lt;code&gt;application.css&lt;/code&gt; these are all separate top-level CSS files that we will need to convert to the new format.&lt;/p&gt;

&lt;p&gt;For each file, (or just &lt;code&gt;application.[s]css&lt;/code&gt;) you should do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If you haven't already, convert the files to use SCSS's &lt;code&gt;@import&lt;/code&gt; syntax instead of the sprockets magic comments like &lt;code&gt;*= require_self&lt;/code&gt; or &lt;code&gt;*= require_tree&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rename each file to match this format &lt;code&gt;&amp;lt;name&amp;gt;.sass.scss&lt;/code&gt; (so &lt;code&gt;application.scss&lt;/code&gt; would become &lt;code&gt;application.sass.scss&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3 - Run the Installation (And Then Fix What It Broke)
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;./bin/rails css:install:sass&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If you receive an overwrite warning for &lt;code&gt;app/assets/stylesheets/application.sass.scss&lt;/code&gt;, you can respond with &lt;code&gt;N&lt;/code&gt; when prompted.&lt;/p&gt;

&lt;p&gt;After the installation completes, we need to clean up a few things.&lt;/p&gt;

&lt;p&gt;First, in our case, the installation inserted an extra &lt;code&gt;stylesheet_link_tag&lt;/code&gt; at the bottom of &lt;code&gt;app/views/layout/application.html.erb&lt;/code&gt;. You should delete this extra line.&lt;/p&gt;

&lt;p&gt;Second, while the installation command updates the &lt;code&gt;app/assets/config/manifest.js&lt;/code&gt; file with a few new lines, it often doesn't remove explicit references to any sass files. After the upgrade, ours looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;//= link_tree ../images&lt;/span&gt;
&lt;span class="c1"&gt;//= link_tree ../fonts&lt;/span&gt;
&lt;span class="c1"&gt;//= link_tree ../builds&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: We added the line for &lt;code&gt;fonts&lt;/code&gt; as we use the &lt;code&gt;font-url&lt;/code&gt; helper in our SCSS files. These fonts didn't need to be explicitly included in the manifest before because sprockets would include them dynamically as they were referenced in the source CSS file. After this upgrade sprockets isn't processing the file so it's important that we ensure it's in the manifest.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;build:css&lt;/code&gt; script the installation creates in &lt;code&gt;package.json&lt;/code&gt; is only sufficient if you only have one main &lt;code&gt;application.scss&lt;/code&gt;, if you have other files you need to output, you are going to need to modify the script's contents. If this is the case, my suggestion is to create a new file called &lt;code&gt;bin/build-css&lt;/code&gt; and do something like the following:&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;#!/usr/bin/env bash&lt;/span&gt;

./node_modules/sass/sass.js &lt;span class="se"&gt;\&lt;/span&gt;
  ./app/assets/stylesheets/application.sass.scss:./app/assets/builds/application.css &lt;span class="se"&gt;\&lt;/span&gt;
  ./app/assets/stylesheets/sessions.sass.scss:./app/assets/builds/sessions.css &lt;span class="se"&gt;\&lt;/span&gt;
  ./app/assets/stylesheets/staff.sass.scss:./app/assets/builds/staff.css &lt;span class="se"&gt;\&lt;/span&gt;
  ./app/assets/stylesheets/marketing.sass.scss:./app/assets/builds/marketing.css &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-source-map&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--load-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;node_modules &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;$@&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;$@&lt;/code&gt; at the bottom ensures we pass along any additional arguments like &lt;code&gt;--watch&lt;/code&gt; when this is invoked via &lt;code&gt;bin/dev&lt;/code&gt; (more on that later).&lt;/p&gt;

&lt;p&gt;Now in the &lt;code&gt;package.json&lt;/code&gt; file do the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:css"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./bin/build-css"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Don't forget to also run &lt;code&gt;chmod 755 ./bin/build-css&lt;/code&gt; in your terminal before moving on to the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Handle &lt;code&gt;asset-url&lt;/code&gt; And Friends
&lt;/h3&gt;

&lt;p&gt;Often in Rails, you need to reference files from the &lt;code&gt;app/assets/images&lt;/code&gt; or &lt;code&gt;app/asset/fonts&lt;/code&gt; folders directly in CSS. Since sprockets computes hashes for each asset, you can't just hard-code the name of the asset in there. To work around this, sprockets introduced helper functions like &lt;code&gt;asset-url&lt;/code&gt;, &lt;code&gt;font-url&lt;/code&gt;, and &lt;code&gt;image-url&lt;/code&gt; that resolve the relative path to the asset correctly.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dart-sass&lt;/code&gt; has no knowledge of sprockets, so before we finalize the build we need sprockets to run through each file quickly and add these assets paths in. &lt;a href="https://github.com/rails/sprockets-rails/pull/476" rel="noopener noreferrer"&gt;While official support for this is pending&lt;/a&gt;, we arrived at a workaround that seems to do the trick:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# config/initializers/asset_url_processor.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssetUrlProcessor&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:environment&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;context_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:data&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/(\w*)-url\(\s*["']?(?!(?:\#|data|http))([^"'\s)]+)\s*["']?\)/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_match&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="s2"&gt;"url(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asset_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="vg"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Sprockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_postprocessor&lt;/span&gt; &lt;span class="s2"&gt;"text/css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;AssetUrlProcessor&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This regex will match these url functions and convert their contents to the appropriate location of the asset on disk in both development and production. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Test It Out With &lt;code&gt;bin/dev&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Before trying this out, you'll likely want to clear out any sprockets cache in &lt;code&gt;tmp/&lt;/code&gt;. To do that, you can simply run &lt;code&gt;bin/rake tmp:clear&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As part of the earlier &lt;code&gt;./bin/rails css:install:sass&lt;/code&gt; command, a new file called &lt;code&gt;bin/dev&lt;/code&gt; was created. Additionally, a new gem dependency called &lt;code&gt;foreman&lt;/code&gt; and its associated config file &lt;code&gt;Procfile.dev&lt;/code&gt; was installed.&lt;/p&gt;

&lt;p&gt;By running &lt;code&gt;bin/dev&lt;/code&gt; you are now invoking &lt;code&gt;foreman&lt;/code&gt; which will read the &lt;code&gt;Profile&lt;/code&gt; and simultaneously run the &lt;code&gt;rails server&lt;/code&gt; and the &lt;code&gt;yarn build:css --watch&lt;/code&gt; commands. This should give you a very similar development experience to the original setup where you can make changes to CSS files and, after a refresh, those changes will be immediately reflected in the browser.&lt;/p&gt;

&lt;p&gt;If all went well, &lt;code&gt;bin/dev&lt;/code&gt; should start right up and a visit to your app locally should "just work."&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits &amp;amp; Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;A big thank you to &lt;a href="https://github.com/alxjrvs" rel="noopener noreferrer"&gt;Alex Jarvis&lt;/a&gt; for leading the charge on this upgrade at Kolide and collaborating with me.&lt;/p&gt;

&lt;p&gt;I hope you found this guide useful. If you found any errors in this guide or suggestions to improve it, please reach out in the comments or hit me up on twitter &lt;a href="https://twitter.com/jmeller" rel="noopener noreferrer"&gt;@jmeller&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://angel.co/company/kolideco/jobs/1638302-rails-engineer" rel="noopener noreferrer"&gt;Oh, and we're hiring!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>sass</category>
      <category>ruby</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Three Myths about Honest Security</title>
      <dc:creator>Antigoni Sinanis</dc:creator>
      <pubDate>Tue, 08 Dec 2020 22:20:16 +0000</pubDate>
      <link>https://dev.to/kolide/three-myths-about-honest-security-2dmn</link>
      <guid>https://dev.to/kolide/three-myths-about-honest-security-2dmn</guid>
      <description>&lt;p&gt;Today at Kolide, we published our guide to &lt;a href="https://honest.security/"&gt;Honest Security&lt;/a&gt;. It’s our North Star for Kolide and represents our vision of the future for the endpoint security and device management.&lt;/p&gt;

&lt;p&gt;Honest Security focuses on the following five tenets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The values your organization stands behind &lt;strong&gt;should be well-represented in your security program.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A positive working relationship&lt;/strong&gt; between the end-user and the security team is incredibly valuable and worth fostering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This relationship is built on a foundation of trust that is demonstrated through &lt;strong&gt;informed consent and transparency.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The security team should &lt;strong&gt;anticipate and expect that end-users use their company owned devices for personal activities&lt;/strong&gt; and design their detection capabilities with this in mind.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;End-users are capable of making &lt;strong&gt;rational and informed decisions about security risks&lt;/strong&gt; when educated and honestly motivated.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://hackervalleystudio.podbean.com/e/episode-109-making-security-honest-with-jason-meller/"&gt;Listen to the podcast about Honest Security at Hacker Valley Studio.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While much of the free guide we posted focuses on what Honest Security is and how it should work mechanically, I imagine there are many IT and security practitioners out there that might be automatically shut off to something like this due to previous bad experiences.&lt;/p&gt;

&lt;p&gt;After chatting with a few folks who have read the tenets, I’ve noticed there are already some common misconceptions forming about the Honest Security approach. As we launch Honest Security, I thought I would author a supplementary post that dispels some of the myths behind the methodology.&lt;/p&gt;

&lt;h3&gt;
  
  
  Myth #1: Honest Security is incompatible with Device Management
&lt;/h3&gt;

&lt;p&gt;Many folks believe Honest Security advocates for an approach that is fundamentally incompatible with device management like MDM. You could hardly be blamed if you do; I have been advocating strongly against blanket device management since the inception of Kolide. Since 2019, our thinking has evolved.&lt;/p&gt;

&lt;p&gt;In the chapter six &lt;a href="https://honest.security/compliance/"&gt;Achieving Compliance Objectives,&lt;/a&gt; we acknowledge that education isn’t enough to move the needle towards acceptable levels of adherence to the company’s objectives. To that end, we suggest generating predictable and proportionate consequences that can be applied to end-users who are not heeding the important recommendations of the security team. We go on to describe the concept of &lt;strong&gt;Opt-in Management.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;While this process is effective, there are just some people who will continually find themselves always on the brink of the consequence activating (or worse, serial offenders). In some situations, these users may do much better with the recommendations they regularly fail to implement on time if the security team could just do it for them. This is where Honest Security can allow the users to opt-in to traditional device management solutions (where applicable) and not have to worry about getting locked out of critical services or accounts.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We feel so strongly about this, we are working on an &lt;a href="https://www.kolide.com/mdm/"&gt;Honest MDM solution for our customers&lt;/a&gt;. The MVP is almost complete!&lt;/p&gt;

&lt;h3&gt;
  
  
  Myth #2: Honest Security blinds security teams in the name of privacy
&lt;/h3&gt;

&lt;p&gt;Critics of Honest Security approach often get defensive about its strong push away from blanket collection of data that might be useful later. Many folks I deeply respect remind me that if it were not for a certain piece of data they weren’t sure they should gather, they would have never been able to detect &amp;lt;&lt;em&gt;insert really bad thing&lt;/em&gt;&amp;gt;.&lt;/p&gt;

&lt;p&gt;While Honest Security definitely advocates for being mindful and intentional about the data you collect, it primarily champions for an informed-consent and transparency based approach. If the security team really does think a piece of data is important, then we should be up-front about it. If that data could be dangerous or extremely personal (like GPS coordinates), then informed-consent is the best option.&lt;/p&gt;

&lt;p&gt;The best summary of his position is in Chapter 4. &lt;a href="https://honest.security/collecting-data-honestly/"&gt;Collecting Data Honestly&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Do you know &lt;strong&gt;if&lt;/strong&gt; your organization &lt;strong&gt;looks&lt;/strong&gt; at your web browser history?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I want to clarify that I am not asking them whether or not the company collects web browsing history. I am asking them whether they know, with 100% certainty, whether the security team is or is not. It is one thing to know whether your organization can view your browsing history, and another to know if they do. Also notice I didn’t ask them if the organization usually looks. One person, looking once because they were curious,&lt;/em&gt; is looking.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;After making these clarifications, it is my experience that there are three camps of people who can still emphatically answer “yes.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[…]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The third camp are the folks who can answer “yes”, because they know exactly what tools are installed on their devices and what the tools are capable of collecting. More importantly, they know they can independently verify how the security and IT team is using these tools in practice. They know if the security team is looking at their web browser history because the tools the security team uses require them to know. These are people who work for companies that practice Honest Security.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Myth #3: Honest Security hurts Insider Threat detection and deterrence
&lt;/h3&gt;

&lt;p&gt;Many folks think the quality of their insider threat defense strategy is inextricably tied to the act of obfuscating exactly how the endpoint security team performs its detection mission. We address this myth directly in the fourth chapter, &lt;a href="https://honest.security/collecting-data-honestly/"&gt;Collecting Data Honestly&lt;/a&gt;. In there you will find a section aptly titled, “The Insider Threat”. (reproduced below)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The most common argument I see against transparency is that it gives bad-actors within your organization an advantage. The rationale is that an insider threat might be able to identify gaps in the security team’s detection capabilities and systematically abuse them to complete their mission. I disagree. As we all know security through obscurity rarely works. Also, it’s much more likely that this transparency and regular contact will instead serve as a deterrent. Unlike end-users who are making unforced errors, malicious insiders are afraid of being caught. The more interactions they have with a team practicing Honest Security, the more uncomfortable they will get.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part of Honest Security is trusting end-users because they are our colleagues. If you build a dystopian and cynical security program born out of fear, mistrust, and suspicion, then you will inevitably make your fellow-employees your enemies. The positive working relationship we are advocating for in this guide cannot exist under such a program. Only you can judge if that trade-off is ultimately worthwhile.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I hope this short post cleared up some of the myths we’ve seen in the early hours of promoting this honest approach to security. While we’ve thought very carefully about what we want to include in our guide, it’s a document we expect to evolve based on the feedback, opinions, and most importantly, the experiences of security/IT professionals all over the world.&lt;/p&gt;

&lt;p&gt;The Honest Security Guide can be found at &lt;a href="https://honest.security"&gt;https://honest.security&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;-&lt;em&gt;Jason Meller, Co-Founder and CEO of &lt;strong&gt;Kolide, Inc.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>honestsecurity</category>
      <category>security</category>
    </item>
  </channel>
</rss>
