<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: AuthZed</title>
    <description>The latest articles on Forem by AuthZed (@authzed).</description>
    <link>https://forem.com/authzed</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%2F7382%2Fbf52b29b-929b-4de6-ad15-4694ac5b0eba.png</url>
      <title>Forem: AuthZed</title>
      <link>https://forem.com/authzed</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/authzed"/>
    <language>en</language>
    <item>
      <title>Safeguarding Your Data When Using DeepSeek R1 In RAG Pipelines - Part II</title>
      <dc:creator>Sohan</dc:creator>
      <pubDate>Fri, 31 Jan 2025 20:04:15 +0000</pubDate>
      <link>https://forem.com/authzed/safeguarding-your-data-when-using-deepseek-r1-in-rag-pipelines-part-ii-2cli</link>
      <guid>https://forem.com/authzed/safeguarding-your-data-when-using-deepseek-r1-in-rag-pipelines-part-ii-2cli</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/authzed/safeguarding-your-data-when-using-deepseek-r1-in-rag-pipelines-part-1-31d2"&gt;In Part I&lt;/a&gt; we learnt about why we should secure our RAG pipelines with Fine Grained Authorization, and also what are the methods to do so.&lt;/p&gt;

&lt;p&gt;Let's now get our hands dirty and write code to actually do so. &lt;/p&gt;

&lt;p&gt;We'll authorizing access to view blog articles and get information from it. We'll see what happens when a request is authorized and when it isn't. Here's our RAG pipeline with the software we're using. &lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qxoxhtvlpopna9qg86t.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qxoxhtvlpopna9qg86t.png" alt="RAG software used" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Let's Talk Schema!
&lt;/h3&gt;

&lt;p&gt;Let's set up our permissions system. Once you've installed SpiceDB, create a schema about two objects: &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;articles&lt;/code&gt;. The setup is simple - users can be "viewers" of articles, and if you're tagged as a viewer, you get the all-access pass to view that article.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;authzed.api.v1&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;WriteSchemaRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;#change to bearer_token_credentials if you are using tls
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;grpcutil&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;insecure_bearer_token_credentials&lt;/span&gt;

&lt;span class="n"&gt;SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;definition user {}

definition article {
    relation viewer: user

    permission view = viewer
}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SPICEDB_ADDR&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;insecure_bearer_token_credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SPICEDB_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WriteSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WriteSchemaRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SCHEMA&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write schema error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Write a Relationship
&lt;/h3&gt;

&lt;p&gt;Alright, first things first - we're gonna tell SpiceDB that &lt;strong&gt;Tim&lt;/strong&gt; should be able to peek at &lt;code&gt;document 123&lt;/code&gt; and &lt;code&gt;document 456&lt;/code&gt;. Think of it like giving Tim a special pass to view these specific files.&lt;/p&gt;

&lt;p&gt;This is how we write a Relationship in SpiceDB. Once we've done this, SpiceDB will know exactly what Tim can and can't see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;authzed.api.v1&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Relationship&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RelationshipUpdate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SubjectReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;WriteRelationshipsRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WriteRelationships&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;WriteRelationshipsRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nc"&gt;RelationshipUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RelationshipUpdate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OPERATION_TOUCH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;relationship&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;viewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SubjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tim&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;RelationshipUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RelationshipUpdate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OPERATION_TOUCH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;relationship&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;456&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;viewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SubjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tim&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write relationships error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Writing to our Vector DB
&lt;/h3&gt;

&lt;p&gt;Pinecone is a vector database where we store our embeddings. Let's set up our Pinecone serverless index - don't worry, it's not as complicated as it sounds!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#from pinecone.grpc import PineconeGRPC as Pinecone
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pinecone&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ServerlessSpec&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pinecone&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pinecone&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;pc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pinecone&lt;/span&gt;&lt;span class="p"&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="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PINECONE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;index_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;oscars&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dimension&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cosine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ServerlessSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's where it gets fun - we're going to create a totally made-up fact: "&lt;strong&gt;Bill Gates won the 2025 Oscar for best football movie.&lt;/strong&gt;" (I know, wild right? 😄). We're using this made-up fact to show how RAG handles information that LLMs don't already know about.&lt;/p&gt;

&lt;p&gt;We'll also add a little tag (&lt;code&gt;article_id&lt;/code&gt;) to keep track of where this info came from. This is super important because it helps us link everything back to our permission system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_pinecone&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PineconeEmbeddings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_pinecone&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PineconeVectorStore&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.schema&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Create a Document object that specifies our made up article and specifies the document_id as metadata.
&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bill Gates won the 2025 Oscar for best football movie&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page_content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# Initialize a LangChain embedding object.
&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;multilingual-e5-large&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PineconeEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pinecone_api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PINECONE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;namespace_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;oscar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Upsert the embedding into your Pinecone index.
&lt;/span&gt;&lt;span class="n"&gt;docsearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PineconeVectorStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;namespace_name&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Checking Tim's VIP Permissions
&lt;/h3&gt;

&lt;p&gt;Now comes the cool part! We'll ask SpiceDB what documents Tim can actually see. This is how you can check for permissions and look up resources in SpiceDB. Here we're using the &lt;code&gt;LookupResources&lt;/code&gt; API to get a list of articles that Tim has permission to view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;authzed.api.v1&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;LookupResourcesRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SubjectReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SubjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tim&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lookupArticles&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LookupResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;LookupResourcesRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;view&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;resource_object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lookupArticles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;authorized_articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;authorized_articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resource_object_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lookup error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Article IDs that Tim is authorized to view:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authorized_articles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Article IDs that Tim is authorized to view:
['123', '456']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that sorted, we can chat with our DeepSeek R1 model, but only about stuff Tim's allowed to see. It's like having a really smart assistant who's also great at keeping secrets! &lt;/p&gt;

&lt;p&gt;Quick side notes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We're using OpenRouter to access the DeepSeek R1 LLM&lt;/li&gt;
&lt;li&gt;We're sticking with OpenAI for the embeddings part because they're pretty much the gold standard for this kind of thing.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_community.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIEmbeddings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_pinecone&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PineconeVectorStore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.runnables&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;RunnableParallel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RunnablePassthrough&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Custom wrapper for OpenRouter
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;openai_api_base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;openai_api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;openai_api_base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://openrouter.ai/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;openai_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENROUTER_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;openai_api_base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;openai_api_base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="n"&gt;openai_api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;openai_api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define the ask function
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="c1"&gt;# Initialize a LangChain object for DeepSeek via OpenRouter.
&lt;/span&gt;    &lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek/deepseek-r1-distill-llama-70b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Initialize a LangChain object for a Pinecone index with OpenAI embeddings model.
&lt;/span&gt;    &lt;span class="n"&gt;knowledge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PineconeVectorStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_existing_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;namespace_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;openai_api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;dimensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-3-large&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Initialize a retriever with a filter that restricts the search to authorized documents.
&lt;/span&gt;    &lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;knowledge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_retriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;search_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$in&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;authorized_articles&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Initialize a string prompt template for context and question.
&lt;/span&gt;    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Answer the question below using the context:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Context: {context}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Question: {question}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Answer:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Combine retrieval and prompt to pass through DeepSeek LLM via OpenRouter
&lt;/span&gt;    &lt;span class="n"&gt;retrieval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RunnableParallel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RunnablePassthrough&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retrieval&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Example question
&lt;/span&gt;    &lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Who won the 2025 Oscar for best football movie?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Prompt: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# Invoke the ask function
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt: 

Who won the 2025 Oscar for best football movie?
Bill Gates won the 2025 Oscar for best football movie.

Answer: Bill Gates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There you go! Our RAG pipeline got this information that LLM didn't already know about. &lt;/p&gt;

&lt;h3&gt;
  
  
  5. What Happens When Tim's Pass Expires?
&lt;/h3&gt;

&lt;p&gt;Let's shake things up and see what happens when Tim loses access to some docs.&lt;/p&gt;

&lt;p&gt;First step: we're gonna revoke Tim's viewing privileges fora document. This code snippet updates a relationship between Tim and &lt;code&gt;document 123&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WriteRelationships&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;WriteRelationshipsRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nc"&gt;RelationshipUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RelationshipUpdate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OPERATION_DELETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;relationship&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;viewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SubjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;object_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tim&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write relationships error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Then&lt;/span&gt; &lt;span class="n"&gt;we&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ll double-check what Tim can still see.

#this function was defined above
try:
        resp = lookupArticles()

        authorized_articles = []

        async for response in resp:
                authorized_articles.append(response.resource_object_id)
except Exception as e:
    print(f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lookup error: {type(e).__name__}: {e}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)

print(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Documents that Tim can view:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)
print(authorized_articles)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documents that Tim can view:
['456']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tim's lost access to &lt;code&gt;document_123&lt;/code&gt; which had the vital piece of info about the "2025 Oscar for Best Football Movie".&lt;/p&gt;

&lt;p&gt;Time to try our query again!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#this function was defined above
&lt;/span&gt;&lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt: 

Who won the 2025 Oscar for best football movie?
The 2025 Oscars, which honored films released in 2024, did not include a category for "best football movie." The Academy Awards do not have a specific category dedicated to sports films or football-themed movies. Therefore, no award was given in that non-existent category. It's possible there might be confusion with another award ceremony that recognizes sports-related films. 

Answer: No one won an Oscar for best football movie in 2025 because the Academy Awards do not have such a category.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And... plot twist! The system won't spill the beans anymore because Tim's not authorized to see that document. It's like trying to read a book that's been checked out of the library. &lt;/p&gt;

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

&lt;p&gt;This was a step-by-step guide on how you can have fine grained authorization for your RAG pipelines. Do you have other ways of writing authorization logic for your LLMs and RAGs? Let me know in the comments! &lt;/p&gt;

&lt;p&gt;As for the image: Well this is what DALL-E thinks what "Bill Gates won the 2025 Oscar for best football movie" looks like! &lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo32566erb8w5rsgwpim6.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo32566erb8w5rsgwpim6.png" alt="Best football move" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As promised, here is a &lt;a href="https://github.com/authzed/workshops/blob/deepseek/secure-rag-pipelines/01-rag.ipynb" rel="noopener noreferrer"&gt;link to the working Jupyter Notebook&lt;/a&gt;. Have fun! &lt;/p&gt;

</description>
      <category>deepseek</category>
      <category>security</category>
      <category>authorization</category>
      <category>llm</category>
    </item>
    <item>
      <title>Safeguarding Your Data When Using DeepSeek R1 In RAG Pipelines - Part 1</title>
      <dc:creator>Sohan</dc:creator>
      <pubDate>Fri, 31 Jan 2025 19:38:58 +0000</pubDate>
      <link>https://forem.com/authzed/safeguarding-your-data-when-using-deepseek-r1-in-rag-pipelines-part-1-31d2</link>
      <guid>https://forem.com/authzed/safeguarding-your-data-when-using-deepseek-r1-in-rag-pipelines-part-1-31d2</guid>
      <description>&lt;p&gt;DeepSeek is the talk of the tech world right now, and rightfully so!&lt;/p&gt;

&lt;p&gt;If you're implementing the DeepSeek Large Language Model (or any LLM for that matter) in your &lt;strong&gt;Retrieval-Augmented Generation&lt;/strong&gt; (RAG) Pipeline, you have to ensure that the LLM accesses only the data its authorized to. &lt;/p&gt;

&lt;p&gt;This guide will walk you through the nuts and bolts of securing your RAG pipelines with Fine Grained Authorization while also about making your queries secure and super efficient! There's also a notebook linked at the end if you want to look at some code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This example uses DeepSeek R1 but works with any LLM. Using Authorization for RAG Pipelines is a best practice regardless of which LLM and Emebedding model you are using. &lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo32566erb8w5rsgwpim6.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo32566erb8w5rsgwpim6.png" alt="Best football move" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How is this image relevant? It's relevant to our RAG Pipeline and you'll find out how at the end of this guide.&lt;/em&gt; 🤭&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software used in this guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek R1 LLM (through OpenRouter) &lt;/li&gt;
&lt;li&gt;OpenAI for Embeddings &lt;/li&gt;
&lt;li&gt;SpiceDB for permissions&lt;/li&gt;
&lt;li&gt;Pinecone as our Vector Database&lt;/li&gt;
&lt;li&gt;Langchain for language model integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why is this important?
&lt;/h3&gt;

&lt;p&gt;Because we now need to think of &lt;strong&gt;Day2 AI Ops&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Enterprises are working extra hard to keep sensitive info (like personal details and company secrets) from leaking out. The go-to solution? Setting up some solid guardrails around RAG to keep data safe while making sure everything runs smoothly and efficiently.&lt;/p&gt;

&lt;p&gt;To get these guardrails just right, you need to set up some smart permission systems that can keep track of who can see what and which resources they can access. &lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;Let me break down how a typical RAG pipeline works - it's pretty straightforward with two main parts:&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fekj2niaubtpur4t4ezp8.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fekj2niaubtpur4t4ezp8.png" alt="typical rag pipeline" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Ingestion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of this as preparing your knowledge base. We grab all sorts of data, clean it up a bit, turn it into embeddings (vectors that represent real-world objects), and store them in a vector database. It's like organizing your digital library, where each book (or document) gets a special tag - like "&lt;em&gt;document123&lt;/em&gt;" - so we can keep track of where everything came from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Query &amp;amp; Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's where it gets fun! When someone asks the chatbot a question, it transforms their question into the same kind of embedding format and goes hunting through the vector database for relevant matches. It's like having a super-smart librarian who knows exactly where to look! Once it finds the answer, the chatbot feeds this information to the LLM, which crafts a nice, helpful response based on what it found.&lt;/p&gt;

&lt;p&gt;But here's the catch - and it's a big one - this setup is missing something crucial: authorization checks! 🚨&lt;/p&gt;

&lt;p&gt;For example, if someone who shouldn't have access to sensitive financial data asks "What was our Q4 revenue?", they might get an answer they're not supposed to see. Not ideal, right?&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization, ReBAC &amp;amp; SpiceDB
&lt;/h3&gt;

&lt;p&gt;In case you're new to the world of AuthZ, here's a quick primer: &lt;/p&gt;

&lt;p&gt;Authorization determines whether you have permission to access a resource. Traditional models like Role-Based Access Control (RBAC) work well for simple setups, but as systems grow more complex, defining permissions based on roles alone can get messy. That’s where &lt;strong&gt;Relationship-Based Access Control&lt;/strong&gt; (ReBAC) comes in. Instead of just assigning roles, ReBAC uses relationships—like “Alice is a manager of Project X” or “Bob is a friend of Charlie”—to determine access dynamically. This makes it ideal when it comes to securing your RAG pipelines. &lt;/p&gt;

&lt;p&gt;This guide uses &lt;a href="https://github.com/authzed/spicedb/" rel="noopener noreferrer"&gt;SpiceDB&lt;/a&gt;, a powerful, open-source database designed to handle ReBAC at scale. Inspired by Google’s Zanzibar (which powers Google's Authorization systems across Docs, YouTube and more), SpiceDB lets you define and enforce complex access rules efficiently. With it, you can model relationships between users and resources, then perform lightning-fast permission checks. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three things about SpiceDB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's a quick TL;DR of how SpiceDB works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema&lt;/strong&gt;: This defines the types of objects found, how those objects relate to one another, and the permissions that can be computed off of those relations. Developers can read and write a schema based on their use-case and then store &amp;amp; query data. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relationships&lt;/strong&gt;: Relationships are what binds together a Subject and a Resource via a Relation. A functioning Permissions System that uses ReBAC is the combination of Schema and Relationships &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Checks &amp;amp; Lookups&lt;/strong&gt;: Now that we have a schema and relationships in the database, we can issue checks on whether a subject has a permission on a specific resource, or what resources a subject can access whether via a computed permission or relation membership.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Adding Authorization to your RAG Pipeline
&lt;/h3&gt;

&lt;p&gt;Now there are two approaches to adding AuthZ to your RAG Pipeline. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Post-filter Authorization&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnn0cxdpgg14afnj1k9tp.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnn0cxdpgg14afnj1k9tp.png" alt="Post-filter Authorization" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So here's the deal: each embedding can have meta data showing which document it came from (like &lt;code&gt;document123&lt;/code&gt;). We use this to check if you're actually allowed to see that content.&lt;/p&gt;

&lt;p&gt;The process? We can perform a check for each relevant embedding to see if the user has permissions to view the document that the embedding originated from. You can specify the contexts you require: Ex: “I need 5 pieces of additional context before I make the prompt to the LLM” or “exhaust all the embeddings returned”&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pre Filter Authorization&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6k1enafha7nfh6qdwkzf.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6k1enafha7nfh6qdwkzf.png" alt="Pre Filter Authorization" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we make a query and embed it. But before diving in, we check with our permissions system to see what stuff we're actually allowed to peek at. It gives us back a list of all the documents we can access.&lt;/p&gt;

&lt;p&gt;Then we just use that list as our filter, grab all the relevant embeddings we're allowed to see, and boom - we're good to go! That's what we'll be playing with in this guide. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-step guide
&lt;/h2&gt;

&lt;p&gt;Where's the code you ask? Well that's in &lt;a href="https://dev.to/sohan26/safeguarding-your-data-when-using-deepseek-r1-in-rag-pipelines-part-ii-2cli"&gt;Part II of this guide&lt;/a&gt;. Now that you've understood the concepts, here's the step-by-step guide to securing your RAG Pipelines.&lt;/p&gt;

</description>
      <category>deepseek</category>
      <category>authorization</category>
      <category>security</category>
      <category>rag</category>
    </item>
    <item>
      <title>Don't use JWT for Authorization!</title>
      <dc:creator>Sohan</dc:creator>
      <pubDate>Tue, 14 Jan 2025 14:30:00 +0000</pubDate>
      <link>https://forem.com/authzed/dont-use-jwt-for-authorization-1io5</link>
      <guid>https://forem.com/authzed/dont-use-jwt-for-authorization-1io5</guid>
      <description>&lt;p&gt;What's with the shouty title? Well, I wanted to grab your attention and get straight to the point: &lt;/p&gt;

&lt;p&gt;🗣️🗣️ &lt;strong&gt;Don't use JWT for your backend authorization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Look, there's a time and place for every piece of technology and the tricky part is determining if your use case actually is the time and place. Hopefully this post will walk you through why JWTs might not be your best friend, and the rare cases where they actually make sense.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔄 Quick Crash Course: What's a JWT?
&lt;/h3&gt;

&lt;p&gt;So, JWT (pronounced "&lt;em&gt;jot&lt;/em&gt;") stands for JSON Web Token. It's part of this whole family of specs called JOSE (no way!) that deal with encrypting and signing JSON. JWT is the cool kid of the family - it's defined in RFC7519 and gets all the attention. Why? Because while its siblings (JWA, JWE, JWK, JWS) handle the nitty-gritty encryption stuff, JWT is the one carrying the actual payload.&lt;/p&gt;

&lt;p&gt;Think of a JWT as a JSON object wearing a fancy coat (some headers) and carrying an ID card (a signature) to prove it's legit. It's got these things called "claims" - like when it expires (&lt;code&gt;exp&lt;/code&gt;), who created it (&lt;code&gt;iss&lt;/code&gt;), who it's for (&lt;code&gt;aud&lt;/code&gt;), and so on. The most popular claim for authorization is called "scope", which, fun fact, isn't even from JOSE - it's borrowed from OAuth2. Most developers end up mixing and matching these pieces like a authorization puzzle until something works.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚔️ The New Enemy Problem: JWT's Achilles' Heel
&lt;/h3&gt;

&lt;p&gt;Here's the thing: JWTs have a major weakness - once they're out there, you can't take them back (except waiting for them to expire). It's like giving someone an all-access pass and not being able to revoke it if they go rogue. This becomes super awkward with web sessions - ever tried implementing a proper "logout" with JWTs? Good luck with that! You're basically crossing your fingers hoping users will play nice and throw away their old tokens.&lt;/p&gt;

&lt;p&gt;But wait, it gets worse for backend services. Imagine this: you revoke someone's access on your server, but they're still holding a valid JWT from before. They can keep accessing stuff they shouldn't - this is what the smart folks call the "New Enemy Problem" (first spotted in &lt;a href="https://zanzibar.tech/" rel="noopener noreferrer"&gt;Google's Zanzibar paper&lt;/a&gt;). It's like changing the locks but forgetting about all the spare keys you handed out. Centralized authorization systems fix this by having a central service (think of it as like a bouncer at a bar) checking everyone's credentials in real-time. The New Enemy problem is a really hard and interesting distributed systems problem (and perhaps a future post here)&lt;/p&gt;

&lt;p&gt;An example of the New Enemy problem: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alice removes Bob from the ACL of a document;&lt;/li&gt;
&lt;li&gt;Alice then asks Charlie to add new contents to the document;&lt;/li&gt;
&lt;li&gt;Bob should not be able to see the new contents, but may do so if the ACL check is evaluated with a stale ACL from before Bob's removal&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📏 JWT Scopes: Not as Fine-Grained as You'd Think
&lt;/h3&gt;

&lt;p&gt;While JWTs look good on paper, things get messy in practice. Remember that scope claim I mentioned? It's... kinda vague. The spec basically just says "here's what characters you can use" and calls it a day. You'll see examples such as '&lt;code&gt;email profile phone address&lt;/code&gt;' floating around, and developers often try to get fancy with stuff like '&lt;code&gt;profile:admin&lt;/code&gt;'. But here's the million-dollar question: what does that actually mean? The whole site? Just one user's profile? Even GitHub's REST API has been wrestling with this for ages!&lt;/p&gt;

&lt;p&gt;Modern apps need super specific permissions - we're talking granular stuff like '&lt;code&gt;issue/authzed/spicedb/52:author&lt;/code&gt;' instead of just '&lt;code&gt;issue:author&lt;/code&gt;'. When your users might need access to billions of things, you can't stuff all that into a token that's bouncing between services.&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4nk6v8af945rhi79hbw.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4nk6v8af945rhi79hbw.png" alt="Coarse and Fine Grained Authorization" width="800" height="773"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Centralized authorization is like having a smart assistant who keeps track of everything in one place. Need to check something? Just ask! For example: &lt;a href="https://github.com/authzed/spicedb/" rel="noopener noreferrer"&gt;SpiceDB&lt;/a&gt; does this using something called &lt;strong&gt;ReBAC&lt;/strong&gt; (Relationship-Based Access Control) - it's like a Swiss Army knife that can handle super detailed permissions while still playing nice with other permissions systems such as Role Based Access Control (RBAC),  Attribute Based Access Control (ABAC), and other fancy patterns. Google also uses ReBAC for authorization across their services such as YouTube, Docs, and more. &lt;/p&gt;

&lt;h3&gt;
  
  
  🔮 The Crystal Ball Problem with JWT Authorization
&lt;/h3&gt;

&lt;p&gt;Let's play pretend and say you're cool with using just a few JWT scopes. Even then, you've got a problem: how do you know what permissions you'll need? When your JWT gets created at the front door (like in an API gateway), it needs to predict what every downstream service might want. For anything beyond a super simple setup, that's like trying to predict next week's lottery numbers!&lt;/p&gt;

&lt;p&gt;Plus, if you send a token with too many permissions to the next service, you're basically giving attackers a bigger target to hit. This headache led to the creation of &lt;a href="https://en.wikipedia.org/wiki/Macaroons_(computer_science)" rel="noopener noreferrer"&gt;Macaroons&lt;/a&gt;. These tokens can actually be trimmed down before being passed along - cool idea, right? But in reality, they're so complicated that most folks who tried them ended up saying "thanks, but no thanks." &lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/MZFv62qz8RU"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;Centralized authorization systems take a different approach. They're like "Hey, we know we can't predict the future, so just ask us when you need something!" Sure, you have to make an extra call, but systems like SpiceDB are optimized to keep data in-memory - so latency looks similar to reaching out to any other cache like redis or memcache.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤔 So... Are JWTs Ever the Right Choice?
&lt;/h3&gt;

&lt;p&gt;After all this JWT-bashing, you might think they're completely useless. But there is one scenario where they shine: one-time grants where access cannot be revoked. Though honestly, that's about as rare as finding a unicorn in your backyard!&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6vun4vmmik0bfy46imq.jpeg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6vun4vmmik0bfy46imq.jpeg" alt="Unicorn in the backyard" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What system do you use for your authorization needs? Let me know in the comments below. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE 1:&lt;/strong&gt;&lt;br&gt;
There's a nice discussion in the comments about either adding state to the JWT, or a system of using a denylist for token revocation. Both these approaches have their downsides and can be fraught with errors. Check the comments below for more info&lt;/p&gt;

</description>
      <category>security</category>
      <category>authorization</category>
      <category>webdev</category>
      <category>development</category>
    </item>
    <item>
      <title>How I'm Learning SpiceDB</title>
      <dc:creator>Sohan</dc:creator>
      <pubDate>Tue, 12 Nov 2024 16:30:00 +0000</pubDate>
      <link>https://forem.com/authzed/how-im-learning-spicedb-5dha</link>
      <guid>https://forem.com/authzed/how-im-learning-spicedb-5dha</guid>
      <description>&lt;p&gt;(Cover pic by &lt;a href="https://unsplash.com/@kellysikkema?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Kelly Sikkema&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/yellow-flower-on-gray-surface-pXmyDPziB8w?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  A Life Update
&lt;/h2&gt;

&lt;p&gt;I recently joined &lt;a href="https://dev.to/"&gt;AuthZed&lt;/a&gt; as a Developer Advocate, and I want to document my learning journey for those going through a similar process. &lt;/p&gt;

&lt;p&gt;Here are the 4 steps that helped me ramp up my knowledge of SpiceDB. I hope you'll find these helpful on your own learning journey! &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with the Basics
&lt;/h2&gt;

&lt;p&gt;It's always beneficial to have strong foundational knowledge. In the past, my eagerness to code got the better of me, and I dove headfirst into building something only to backtrack to actually understand how it works. This time, I didn't want to repeat that mistake, so I started with a refresher on &lt;a href="https://authzed.com/blog/authentication-vs-authorization" rel="noopener noreferrer"&gt;Authorization&lt;/a&gt;, and &lt;a href="https://authzed.com/blog/exploring-rebac" rel="noopener noreferrer"&gt;ABAC RBAC &amp;amp; ReBAC&lt;/a&gt;. If these acronyms are new to you, I'd suggest starting here.&lt;/p&gt;

&lt;p&gt;I then read &lt;a href="https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/" rel="noopener noreferrer"&gt;the Google Zanzibar&lt;/a&gt; paper that inspired SpiceDB, and re-read it - this time &lt;a href="https://zanzibar.tech/" rel="noopener noreferrer"&gt;with annotations&lt;/a&gt;. I have to admit - I find it hard to parse academic papers (who doesn't wish for a TikTok-style summary sometimes?) &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbHN6d3FoeHZrOXBiejAxOXRvemdkMnRkcXoxbjYybGlla2ZyY3B5bSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/nDlMLL4IW1OqGtTJ6m/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbHN6d3FoeHZrOXBiejAxOXRvemdkMnRkcXoxbjYybGlla2ZyY3B5bSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/nDlMLL4IW1OqGtTJ6m/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's where this presentation by Jake Moshenko came in really handy. His explanation brings to life all the concepts listed in the paper and reinforces understanding of how Zanzibar works.&lt;/p&gt;

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

&lt;p&gt;Although SpiceDB is inspired by Zanzibar, &lt;a href="https://authzed.com/docs/spicedb/concepts/zanzibar#differences-with-spicedb" rel="noopener noreferrer"&gt;there are some key differences.&lt;/a&gt; Here are some differences in a Q&amp;amp;A format that helped clarify the concepts. If the number of new concepts and terminologies seems overwhelming, that's okay! You don't have to understand all of it from the start, and hopefully, the rest of this article will help with your learning journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Get the Hang of Schema Design
&lt;/h2&gt;

&lt;p&gt;Schema design is central to SpiceDB and was a new concept for me. A schema essentially defines the types of objects in your system, how those objects relate to one another, and the permissions that can be computed from those relations. I started by watching this video on modeling the GitHub permissions system using Schema.&lt;/p&gt;

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

&lt;p&gt;For practice, I used real-life examples (such as Google Groups or a banking system) and sketched out the different users, objects, and relationships between them. Progressing from a basic user-document schema to a complex real-life example provides valuable practice in designing schemas for SpiceDB.&lt;/p&gt;

&lt;p&gt;You can experiment with modeling these in the &lt;a href="https://play.authzed.com/schema" rel="noopener noreferrer"&gt;SpiceDB playground&lt;/a&gt;. I encourage you to try it out.&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22gjzqmibm0l4j5bdg7r.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22gjzqmibm0l4j5bdg7r.jpg" alt="An image of the Google Groups schema handwritten" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pujd8fcm29tq41323ls.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pujd8fcm29tq41323ls.jpg" alt="An image of a handwritten Github scema " width="800" height="1066"&gt;&lt;/a&gt;&lt;br&gt;
(My niece calls Github as Gibbut so that's the name I refer to it now  😎)&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Build Something Starting from a Point of Familiarity
&lt;/h2&gt;

&lt;p&gt;Having worked at companies like Amazon Web Services (AWS) and Fermyon, I have background knowledge in Cloud, Compute, and Serverless technologies. I looked through the documentation for familiar territory and found &lt;a href="https://authzed.com/docs/spicedb/ops/deploying-spicedb-on-eks" rel="noopener noreferrer"&gt;Deploying SpiceDB on Elastic Kubernetes Service.&lt;/a&gt; My experience with Amazon EKS helped me understand how SpiceDB integrates into that system.&lt;/p&gt;

&lt;p&gt;If you come from an application development background, you might prefer starting with one of &lt;a href="https://authzed.com/docs/spicedb/getting-started/client-libraries" rel="noopener noreferrer"&gt;our client libraries&lt;/a&gt; to build a simple app that communicates with a local SpiceDB instance. Our getting started guide &lt;a href="https://authzed.com/docs/spicedb/getting-started/protecting-a-blog" rel="noopener noreferrer"&gt;Protecting A Blog Application&lt;/a&gt; can be particularly helpful. For those with authorization experience, we offer guides on how &lt;a href="https://authzed.com/docs/spicedb/getting-started/coming-from/opa" rel="noopener noreferrer"&gt;SpiceDB compares with Open Policy Agent (OPA)&lt;/a&gt; or a &lt;a href="https://authzed.com/docs/spicedb/getting-started/coming-from/cancancan" rel="noopener noreferrer"&gt;comparison with Ruby on Rails CanCanCan&lt;/a&gt;. Both show different approaches but share some common ground.&lt;/p&gt;

&lt;p&gt;Good time to shout-out that SpiceDB is &lt;a href="https://github.com/authzed/spicedb/" rel="noopener noreferrer"&gt;completely open-source&lt;/a&gt;, and we welcome community contributions! Whether you'd like to suggest improvements, fix documentation typos, or contribute to the community, please feel free to do so. Check out our &lt;a href="https://github.com/authzed/spicedb/labels/good%20first%20issue" rel="noopener noreferrer"&gt;Good First Issues&lt;/a&gt; and join our &lt;a href="https://authzed.com/discord" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdXZxeGR3cWUyY2c0NGtsZWZzNnQ0ZnpvbWU3OXlubHcwNHkyY2RjeSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/EnvjuAOlZEEtbMLKl5/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdXZxeGR3cWUyY2c0NGtsZWZzNnQ0ZnpvbWU3OXlubHcwNHkyY2RjeSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/EnvjuAOlZEEtbMLKl5/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Use AI Strategically
&lt;/h2&gt;

&lt;p&gt;While learning to deploy SpiceDB on Amazon EKS, I encountered some challenges (a natural part of learning) and consulted ChatGPT about these errors. Here's a debugging step that I received:&lt;/p&gt;

&lt;p&gt;(For context: &lt;strong&gt;zed&lt;/strong&gt; is the AuthZed CLI tool)&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u9vorylsz564y6tz6j7.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u9vorylsz564y6tz6j7.png" alt="A chatGPT snippet" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty straightforward, right? &lt;/p&gt;

&lt;p&gt;Well, except that &lt;code&gt;config&lt;/code&gt; is &lt;u&gt;not&lt;/u&gt; a zed CLI command. LLMs can hallucinate and often do so with a lot of confidence. Watch out for inconsistencies like these that could trip you up when copying code from an LLM.&lt;/p&gt;

&lt;p&gt;This highlights an important distinction between "learning something" and "building something". Asking ChatGPT "How do I install SpiceDB on EKS" and then just spamming the copy-paste keys is not the best way to learn something. I can attest to this because it's exactly what I did at the start! Only partway through did I realize that I hadn't achieved what I set out to do and had to backtrack. On the other hand, asking an LLM about how I could start debugging certain errors gave me a good understanding of what's under the hood. Use these tools thoughtfully and purposefully.&lt;/p&gt;
&lt;h2&gt;
  
  
  One Final Thought
&lt;/h2&gt;

&lt;p&gt;I'm on a roll with the advice, so here's one more thing (yes, that's a &lt;a href="https://en.wikipedia.org/wiki/Stevenote" rel="noopener noreferrer"&gt;Stevenote&lt;/a&gt; reference). This has held me in good stead over the years when learning anything new: enjoy the process, the results will follow.&lt;/p&gt;

&lt;p&gt;Happy Learning!&lt;/p&gt;

&lt;p&gt;P.S. Here's a webinar I recorded for CNCF about Deploying SpiceDB in EKS. There's nothing quite like learning in public! 😎&lt;/p&gt;

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

</description>
      <category>authorization</category>
      <category>security</category>
      <category>iam</category>
      <category>permissions</category>
    </item>
    <item>
      <title>Authentication vs. Authorization</title>
      <dc:creator>AuthZed</dc:creator>
      <pubDate>Wed, 10 Jan 2024 10:04:18 +0000</pubDate>
      <link>https://forem.com/authzed/authentication-vs-authorization-k29</link>
      <guid>https://forem.com/authzed/authentication-vs-authorization-k29</guid>
      <description>&lt;h3&gt;
  
  
  Authz and Authn, a primer
&lt;/h3&gt;

&lt;p&gt;As humans, we’ve been guarding our stuff since we first invented locks 6,000 years ago.&lt;/p&gt;

&lt;p&gt;Guarding assets and sharing access to them is human nature. There are two major steps involved in this process: authentication and authorization. First, one must authenticate that they are who they say they are. Then, once someone’s credentials verify their identity, the next question is whether or not that individual is allowed access to the asset.&lt;/p&gt;

&lt;p&gt;Think of it this way:&lt;/p&gt;

&lt;p&gt;You want to go on a trip to another country. You need a passport to verify that you are who you say you are - this is an example of authentication. You also need a visa that gives you permission to enter the other country. This is an example of authorization. The same principles can be applied in computing:&lt;/p&gt;

&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%2Fauthzed.com%2Fimages%2Fupload%2Fbanner-dialog-04.png" 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%2Fauthzed.com%2Fimages%2Fupload%2Fbanner-dialog-04.png" title="Example of user authentication and authorization when interacting with app" alt="Example of user authentication and authorization when interacting with app" width="800" height="1484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authorization and authentication get confused OFTEN - I mean, look at how similar the words are! Instead of having the perspective of authentication vs. authorization, let’s understand how they work together. As you’ll see, you can’t have one without the other.&lt;/p&gt;

&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%2Fauthzed.com%2Fimages%2Fupload%2Fbanner-text-01.png" 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%2Fauthzed.com%2Fimages%2Fupload%2Fbanner-text-01.png" title="Great Authentication + Great Authorization = Solid Security " alt="Great Authentication + Great Authorization = Solid Security" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What is Authentication?
&lt;/h4&gt;

&lt;p&gt;Authentication, in computing, involves verifying the identity of a user, process, or machine. To authenticate a user, we can use one or a combination of three key factors: Knowledge: Something a person knows – i.e. a password/username, a passcode or PIN, or a security question Ownership: Physical token or something a person owns – i.e. a Yubikey, hardware capable of an encrypted handshake (like ACME for Apple devices), or Google Authenticator Inherence: A vital piece of something a user is or does – i.e. biometrics or keystrokes&lt;/p&gt;

&lt;p&gt;Whenever you sign into social media, your email, or any Web-based application, you are using authentication to prove you are who you say you are.&lt;/p&gt;

&lt;h4&gt;
  
  
  The History of Authentication
&lt;/h4&gt;

&lt;p&gt;Authentication has been a part of our world since the dawn of time, but that is another conversation for another day. For now, let’s dive into the history of authentication in modern computing.&lt;/p&gt;

&lt;p&gt;Authentication started all the way back in the 1960’s (60 years ago!) with the first passwords in databases. From there it moved to passwords with a hash, and eventually, encryption. Modern encryption has been around since the 1970’s with RSA asymmetric encryption.&lt;/p&gt;

&lt;h5&gt;
  
  
  T﻿imeline
&lt;/h5&gt;

&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%2Fauthzed.com%2Fimages%2Fupload%2Fhistory-of-authentication-computing-1-.png" 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%2Fauthzed.com%2Fimages%2Fupload%2Fhistory-of-authentication-computing-1-.png" title="Timeline of Authentication milestones" alt="Timeline of Authentication milestones" width="800" height="3000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Timeline is based on an excellent piece - “&lt;a href="https://www.geekwire.com/2018/digital-authentication-human-beings-history-trust/" rel="noopener noreferrer"&gt;Digital authentication: The past, present and uncertain future of the keys to online identity&lt;/a&gt;” on GeekWire, with a few new additions by the authors]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We don’t know where authentication will go in the future but as technology continues to advance, the ability to identify an individual user will get more complicated. This rings true for all Web applications and services, but even more so for highly secure or zero trust environments such as government platforms and healthcare data. Authentication is the passport to our digital world.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization
&lt;/h3&gt;

&lt;p&gt;If authentication is our passport, authorization is our visa. Authorization is when one entity grants permission to another entity to engage with a resource within a set of boundaries. Authorization has become increasingly difficult for application developers, often causing performance issues when deployed at scale and blocking feature development.&lt;/p&gt;

&lt;p&gt;As our digital footprint has grown, experts have developed new access control and permissions management models to meet the needs of feature hungry teams. Since the advent of the original Access Control List Model (ACL) in the 1960s – similar to an “invite only” guest list – we have designed increasingly abstract authorization systems.&lt;/p&gt;

&lt;h5&gt;
  
  
  T﻿imeline
&lt;/h5&gt;

&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%2Fauthzed.com%2Fimages%2Fupload%2Fhistory-of-authorization-computing.png" 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%2Fauthzed.com%2Fimages%2Fupload%2Fhistory-of-authorization-computing.png" title="Timeline of Authorization milestones" alt="Timeline of Authorization milestones" width="800" height="1999"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Moving forward, interest will likely trend toward dynamic, context-aware systems that can adjust permissions in real time. The exploration of decentralized models for authorization that integrates concepts from ReBAC and systems like Zanzibar suggests a future with more secure, efficient, and flexible access control mechanisms.&lt;/p&gt;

&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%2Fauthzed.com%2Fimages%2Fupload%2Fauthentication-vs-authorization-table.png" 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%2Fauthzed.com%2Fimages%2Fupload%2Fauthentication-vs-authorization-table.png" title="Authentication vs Authorization comparison" alt="Authentication vs Authorization comparison" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As technology develops, so will both authentication and authorization. We at AuthZed are excited to be on the forefront of the authorization space, bringing Google’s Zanzibar to the masses with SpiceDB.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="//play.authzed.com"&gt;try out a schema here&lt;/a&gt; and get started with fine-grained access control across all of your environments and applications.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Primer on Modern Enterprise Authorization (AuthZ) Systems</title>
      <dc:creator>AuthZed</dc:creator>
      <pubDate>Wed, 06 Sep 2023 15:37:08 +0000</pubDate>
      <link>https://forem.com/authzed/a-primer-on-modern-enterprise-authorization-authz-systems-4cf2</link>
      <guid>https://forem.com/authzed/a-primer-on-modern-enterprise-authorization-authz-systems-4cf2</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A business operating complex software environments needs to provide end-user experiences that enable and delight users, but never at the cost of a poor security stance. In 2022, a lapse in security fetched &lt;a href="https://www.ibm.com/reports/data-breach" rel="noopener noreferrer"&gt;on average 9.44M for a data breach in the US, and $4.35M globally according to IBM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post aims to help companies with their authorization decisions and systems and share what we see in the market through conversations with companies looking to solve their authorization challenges, specifically authorization that impacts end-user interaction with their products. I’ll cover how authorization became an issue for most companies, the different approaches, an introduction to Google Zanzibar–the solution the market is converging on, the prominent use cases driving businesses to adopt this new approach, and what you can expect when moving to a relationship-based access control system (ReBAC).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is AuthZ?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Because the Internet is, by definition, a networked system that connects users, most Internet-facing software is designed with a multi-user experience in mind. These environments require constructs to facilitate a natural experience that protects users’ data: &lt;strong&gt;authentication&lt;/strong&gt; , commonly referred to as authN, is the key that verifies an application-specific identity, typically called a user, and &lt;strong&gt;authorization&lt;/strong&gt; , commonly referred to as authZ, dictates what doors that key can open for a user.&lt;/p&gt;

&lt;p&gt;AuthZ plays a pivotal role in software security by ensuring that users have the appropriate level of access to different resources and functionalities within a system. At its core, authZ involves granting or denying access rights to specific resources or actions based on the identity and privileges of the user. It is crucial in preventing unauthorized access and verifying that only authorized users can perform specific actions within a system. It serves as a gatekeeper that protects sensitive data and functionalities. By implementing robust authorization mechanisms, developers can control the level of access granted to different users or roles, thereby safeguarding the system from potential security breaches.&lt;/p&gt;

&lt;p&gt;Collectively in the context of a product’s end users, authN and authZ are called Customer Identity and Access Management (CIAM). Authorization is such a foundational part of the digital experience that its underlying design principles have become &lt;a href="https://99percentinvisible.org/about/the-show/" rel="noopener noreferrer"&gt;99% invisible&lt;/a&gt;, even to developers, leading to fundamental challenges as a business scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why AuthZ is an Issue&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A company typically starts by aggregating all user requests into a single piece of software that tightly couples application logic with authZ. As the company’s product gains traction and its user base grows, the focus shifts to distributing the software and scaling infrastructure components, often ignoring a much-needed change to the authorization system. This further embeds an authorization construct not meant to handle a growing number of requirements.&lt;/p&gt;

&lt;p&gt;From the business perspective, the two key limitations of a legacy authorization system are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permissions are inflexible&lt;/strong&gt; : There isn’t a way to easily add additional constructs like &lt;a href="https://authzed.com/blog/user-defined-roles" rel="noopener noreferrer"&gt;user-defined roles&lt;/a&gt;, recursive relationships, &lt;a href="https://authzed.com/blog/abac-on-spicedb-enabling-netflix-complex-identity-types" rel="noopener noreferrer"&gt;attribute-based access control (ABAC)&lt;/a&gt;, or &lt;a href="https://authzed.com/blog/how-small-is-too-small" rel="noopener noreferrer"&gt;fine-grained authorization (FGA)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Siloed permissions:&lt;/strong&gt; as a company grows, it scales revenue by offering additional products; application teams then build bespoke authorization implementations that are hard to reason about and don’t consider a holistic user experience, especially at large scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google set out to fix this problem, along with several “&lt;a href="https://zanzibar.tech/2AT-rbOOg7:R:1h" rel="noopener noreferrer"&gt;unique challenges involving data consistency and scalability&lt;/a&gt;.”&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Google’s Solution to Authorization: Google Zanzibar&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The confluence of business requirements driving the adoption of &lt;a href="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-207.pdf" rel="noopener noreferrer"&gt;zero-trust architectures&lt;/a&gt; and &lt;a href="https://qonsent.com/static/pdf/qonsent-consumer-insights-june2022.pdf" rel="noopener noreferrer"&gt;94% of consumers wanting more control of their data&lt;/a&gt; in near real-time galvanized the search for a modern authorization system. Google’s response was a modern approach that can scale with your business and maintain strict security requirements, now known as &lt;a href="https://authzed.com/blog/what-is-google-zanzibar" rel="noopener noreferrer"&gt;Google Zanzibar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Among the requirements set forth by the Google Zanzibar team is “&lt;a href="https://zanzibar.tech/2MUghfOn_Y:A:1n" rel="noopener noreferrer"&gt;support for a rich set of access control policies as required by both consumer and enterprise applications&lt;/a&gt;” and “&lt;a href="https://zanzibar.tech/2I7pd_DGm2:2C.49Q9UoJW_:J" rel="noopener noreferrer"&gt;establish consistent semantics and user [developer] experience across applications&lt;/a&gt;.” Zanzibar powers authorization across hundreds of Google Products, including Google Calendar, Cloud, Drive, Maps, Photos, and YouTube. Notably, it unlocks unique experiences like &lt;a href="https://zanzibar.tech/2Ry8nS2U5I:0:1t" rel="noopener noreferrer"&gt;cross-product authorization checks,&lt;/a&gt; e.g., Slack’s Gmail extension can check if a recipient has access to a Google Doc, unlocking growth through reduced friction points while maintaining user privacy.&lt;/p&gt;

&lt;p&gt;Google Zanzibar is a relationship-based access control system (ReBAC), meaning that permissions are derived from the existence of a relationship between digital objects and users. This has positive performance implications, especially for recursive permissions, but, importantly, it’s a natural extension of sharing in the real world, which makes it intuitive for most developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use-Cases Driving Adoption of Google Zanzibar
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Product-Led Growth&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To share something is a user choice and subsequent action. Companies we’re speaking with have learned that facilitating frictionless sharing can help onboard additional users to their platforms. For instance, a hiring platform we’re working with implements fine-grained authorization (FGA) so enterprise recruiters are comfortable exposing permissions to hiring managers related to the positions the managers are looking to fill. Hiring managers, in turn, can proactively engage with candidates and increase activity on the platform.&lt;/p&gt;

&lt;p&gt;Another example is adding capabilities that foster more in-app experiences. For instance, a sharing economy company is boosting engagement with its platform by bringing resource management for users into their native applications instead of relying on third-party applications. Most companies we speak with share similar product-led growth initiatives built atop robust authorization.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Breaking into the Enterprise&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Enhancing security and compliance is a key requirement for B2B companies looking to scale revenue within an enterprise market segment, and OWASP’s 2021 report cites &lt;a href="https://owasp.org/Top10/A01_2021-Broken_Access_Control/" rel="noopener noreferrer"&gt;broken access control&lt;/a&gt; as a top security concern. One of the main risks is “exposure of sensitive information to an unauthorized actor,” which is a core tenet of the Google Zanzibar paper; the system "&lt;a href="https://zanzibar.tech/22_E1zFxjc:0.4BawKBUjM:1K" rel="noopener noreferrer"&gt;must ensure consistency of access control decisions to respect user intentions.&lt;/a&gt;”&lt;/p&gt;

&lt;p&gt;Enterprise users also require increased flexibility; these manifest as the following requirements for product teams:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fine-grained authorization (FGA):&lt;/strong&gt; the ability to control resources down to a granular level, e.g., a page in a document, though there is a balance, see &lt;a href="https://authzed.com/blog/how-small-is-too-small" rel="noopener noreferrer"&gt;How small is too small?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-defined Roles and Permissions:&lt;/strong&gt; beyond a typical application-defined Role-Based Access Control (RBAC) system, product teams need to allow end-user admins to create roles and associated permissions for delegating to internal teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recursive relationships:&lt;/strong&gt; at a certain scale, teams start owning teams. This is challenging for a traditional authorization system dealing with permissions stored in a relational database alongside application data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attributes-Based Access Control (ABAC):&lt;/strong&gt; support for dynamic time-bound or otherwise caveated access.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What to Expect When Adopting Relationship-Based Access Control (ReBAC)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A crucial part of ReBAC systems like Google Zanzibar and our own &lt;a href="https://authzed.com/products/spicedb" rel="noopener noreferrer"&gt;Zanzibar-inspired authorization system, SpiceDB,&lt;/a&gt; is storing permissions data in a separate database; product-specific data (e.g. content of a social media post) is stored in the application database, while the data that drives who can edit that data live in the permissions database. If you have an existing authorization flow, you’ll have to translate that data into permissions data.&lt;/p&gt;

&lt;p&gt;Modeling data is probably the most fun and intuitive part. SpiceDB, like other solutions, has a domain-specific language (DSL) called the &lt;a href="https://authzed.com/docs/reference/schema-lang" rel="noopener noreferrer"&gt;SpiceDB Schema Language&lt;/a&gt; for defining the objects you want to create an authorization system for. The permissions schema defines the objects, e.g., users and documents, how they relate to each other, and the permissions those relationships define.&lt;/p&gt;

&lt;p&gt;Since you’re writing permissions data, integration is a big part of the journey. A ReBAC authorization system is delivered over a gRPC or HTTP API; SpiceDB has libraries available in multiple languages to help developers get up to speed quickly. You’ll want to make sure whatever solution you choose delivers a solid developer experience.&lt;/p&gt;

&lt;p&gt;Google Zanzibar doesn’t mention policy, but we’ve learned through our collaboration building &lt;a href="https://authzed.com/blog/abac-on-spicedb-enabling-netflix-complex-identity-types" rel="noopener noreferrer"&gt;Attribute-Based Access Control (ABAC) for Netflix&lt;/a&gt; that pairing policy with ReBAC is a powerful paradigm. An example of this capability is SpiceDB Caveats: &lt;a href="https://authzed.com/blog/caveats" rel="noopener noreferrer"&gt;Caveats: A Scalable Solution for Policy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A common practice is to organize a core team of developers tasked with architecting and executing an overhaul to your authorization system. The effort must be cross-functional; you’ll want platform engineers, application engineers, and product managers to work together to ensure smooth adoption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;Given how popular Google's approach to authorization has become, there are a number of new companies and projects looking to provide Zanzibar-aaS. At AuthZed, we've created a faithful open-source implementation of Google Zanzibar called &lt;a href="https://github.com/authzed/spicedb" rel="noopener noreferrer"&gt;SpiceDB&lt;/a&gt;, and offer managed commercial offerings that make it easy to get into production. Join the community on &lt;a href="https://authzed.com/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or &lt;a href="https://authzed.com/call" rel="noopener noreferrer"&gt;schedule a call&lt;/a&gt; to learn more!&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Reading
&lt;/h2&gt;

&lt;p&gt;If you’re interested in learning more about Authorization and Google Zanzibar, we recommend reading the following posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://authzed.com/blog/what-is-google-zanzibar" rel="noopener noreferrer"&gt;Understanding Google Zanzibar: A Comprehensive Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://authzed.com/blog/fine-grained-access-control" rel="noopener noreferrer"&gt;Fine-Grained Access Control: Can You Go Too Fine?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://authzed.com/blog/exploring-rebac" rel="noopener noreferrer"&gt;Relationship Based Access Control (ReBAC): Using Graphs to Power your Authorization System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://authzed.com/blog/pitfalls-of-jwt-authorization" rel="noopener noreferrer"&gt;Pitfalls of JWT Authorization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Hotspot Caching in Google Zanzibar and SpiceDB</title>
      <dc:creator>AuthZed</dc:creator>
      <pubDate>Wed, 10 May 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/authzed/hotspot-caching-in-google-zanzibar-and-spicedb-37he</link>
      <guid>https://forem.com/authzed/hotspot-caching-in-google-zanzibar-and-spicedb-37he</guid>
      <description>&lt;h2&gt;
  
  
  Anatomy of a Cache Entry
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://zanzibar.tech/258TNEpDfk:0:O" rel="noopener noreferrer"&gt;section 3.2.5&lt;/a&gt; of Google’s Zanzibar paper, the authors say: “We found the handling of hot spots to be the most critical frontier in our pursuit of low latency and high availability.” They go on to describe a system of splitting peer Zanzibar servers into a distributed cache where the cache keys include an evaluation timestamp and servers are selected based on consistent hashing. With SpiceDB, &lt;a href="https://github.com/authzed/spicedb" rel="noopener noreferrer"&gt;AuthZed’s open-source Google Zanzibar implementation&lt;/a&gt;, we’ve created a faithful implementation of this system. In today’s blog post, I want to talk about how this all works under the hood.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"We found the handling of hot spots to be the most critical frontier in our pursuit of low latency and high availability."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Section 3.2.5, Zanzibar: Google’s Consistent, Global Authorization System&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may recall in our &lt;a href="https://authzed.com/blog/check-it-out-2" rel="noopener noreferrer"&gt;Check it Out #2&lt;/a&gt; blog post, Joey demonstrated how we can break down top-level problems into subproblems to make caching easier.&lt;/p&gt;

&lt;p&gt;For a quick refresher, consider the following schema and relationships:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;definition user {}

definition organization {
    relation admin: user
}

definition document {
    relation org: organization

    relation owner: user
    relation reader: user

    permission view = reader + owner + org-&amp;gt;admin
}

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;document:doc1&lt;/code&gt; has &lt;code&gt;org&lt;/code&gt; of &lt;code&gt;organization:org1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document:doc1&lt;/code&gt; has &lt;code&gt;owner&lt;/code&gt; of &lt;code&gt;user:sally&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document:doc1&lt;/code&gt; has &lt;code&gt;reader&lt;/code&gt; of &lt;code&gt;user:billy&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;organization:org1&lt;/code&gt; has &lt;code&gt;admin&lt;/code&gt; of &lt;code&gt;user:francesca&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When evaluating the top level question: “Can &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;view&lt;/code&gt; &lt;code&gt;document:doc1&lt;/code&gt;?”, there are several interesting subproblems:&lt;/p&gt;

&lt;p&gt;“Is &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;reader&lt;/code&gt; of &lt;code&gt;document:doc1&lt;/code&gt;?"&lt;/p&gt;

&lt;p&gt;“Is &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;owner&lt;/code&gt; of &lt;code&gt;document:doc1&lt;/code&gt;?"&lt;/p&gt;

&lt;p&gt;“&lt;code&gt;document:doc1&lt;/code&gt; has &lt;code&gt;org&lt;/code&gt; of &lt;code&gt;organization:org1&lt;/code&gt;. Is &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;admin&lt;/code&gt; of &lt;code&gt;organization:org1&lt;/code&gt;?"&lt;/p&gt;

&lt;p&gt;In Zanzibar, in order to return consistent results, all problems are evaluated at a consistent &lt;a href="https://zanzibar.tech/29gXmSA4ku:2q:5_" rel="noopener noreferrer"&gt;snapshot timestamp&lt;/a&gt;. You can think of these as representing a single moment in time. If we pick an example timestamp &lt;code&gt;12345&lt;/code&gt;, this becomes an implicit clause for our top-level problem and all sub-problems:&lt;/p&gt;

&lt;p&gt;“Can &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;view&lt;/code&gt; &lt;code&gt;document:doc1&lt;/code&gt; at timestamp &lt;code&gt;12345&lt;/code&gt;?”&lt;/p&gt;

&lt;p&gt;“Is &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;reader&lt;/code&gt; of &lt;code&gt;document:doc1&lt;/code&gt; at timestamp &lt;code&gt;12345&lt;/code&gt;?"&lt;/p&gt;

&lt;p&gt;And so on.&lt;/p&gt;

&lt;p&gt;Ignoring &lt;a href="https://authzed.com/blog/caveats" rel="noopener noreferrer"&gt;SpiceDB Caveats&lt;/a&gt; for a moment, each of these questions also has a yes or no answer at any given time. In SpiceDB, we refer to this answer as “permissionship”, with values: &lt;code&gt;PERMISSIONSHIP_HAS_PERMISSION&lt;/code&gt; and &lt;code&gt;PERMISSIONSHIP_NO_PERMISSION&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;For any given problem or sub-problem, at a specified time, the result to the check is &lt;strong&gt;immutable&lt;/strong&gt; : the value will not ever change. Here are the immutable answers to the problems and sub-problems from above:&lt;/p&gt;

&lt;p&gt;“Is &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;reader&lt;/code&gt; of &lt;code&gt;document:doc1&lt;/code&gt;@ &lt;code&gt;12345&lt;/code&gt;?"→ &lt;code&gt;PERMISSIONSHIP_NO_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;“Is &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;owner&lt;/code&gt; of &lt;code&gt;document:doc1&lt;/code&gt;@ &lt;code&gt;12345&lt;/code&gt;?" → &lt;code&gt;PERMISSIONSHIP_NO_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;“&lt;code&gt;document:doc1&lt;/code&gt; has &lt;code&gt;org&lt;/code&gt; of &lt;code&gt;organization:org1&lt;/code&gt;. Is &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;admin&lt;/code&gt; of &lt;code&gt;organization:org1&lt;/code&gt;@ &lt;code&gt;12345&lt;/code&gt;?" → &lt;code&gt;PERMISSIONSHIP_HAS_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;“Can &lt;code&gt;user:francesca&lt;/code&gt; &lt;code&gt;view&lt;/code&gt; &lt;code&gt;document:doc1&lt;/code&gt; @ &lt;code&gt;12345&lt;/code&gt;?” → &lt;code&gt;PERMISSIONSHIP_HAS_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These become cache keys in SpiceDB as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document:doc1#reader@user:francesca@12345&lt;/code&gt; → &lt;code&gt;PERMISSIONSHIP_NO_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document:doc1#owner@user:francesca@12345&lt;/code&gt; → &lt;code&gt;PERMISSIONSHIP_NO_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;organization:org1#admin@user:francesca@12345&lt;/code&gt; → &lt;code&gt;PERMISSIONSHIP_HAS_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document:doc1#reader@user:francesca@12345&lt;/code&gt; → &lt;code&gt;PERMISSIONSHIP_HAS_PERMISSION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It is important to note: if we were to change the evaluation timestamp, the cache keys would change as well and the existing answers would become irrelevant. That is why picking evaluation timestamps becomes so important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking a Good Snapshot Timestamp
&lt;/h2&gt;

&lt;p&gt;In the previous section, I said that a timestamp represents a single unique point in time. While there is &lt;a href="https://en.wikipedia.org/wiki/B-theory_of_time" rel="noopener noreferrer"&gt;some debate about whether the passage of time is actually an illusion&lt;/a&gt;, for our lived experience a single instant of time occurs only once, and if we were randomly picking times, we would have an infinitesimally small chance of picking the exact same time twice. So how do snapshots timestamps help us?&lt;/p&gt;

&lt;p&gt;To make this easier to visualize, we will use the following diagram of a timeline:&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrdetsdty1dc3fhvyh47.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrdetsdty1dc3fhvyh47.png" alt="single request.png" width="734" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the X-axis we have a time continuum from 0 to 10, and on the Y-axis we have the snapshot timestamp that the request was actually evaluated at. In this example our request was sent at time 5, and the snapshot timestamp chosen was also time 5.&lt;/p&gt;

&lt;p&gt;There are several important factors that play together when determining the timestamp at which a problem and all of its subproblems will be evaluated. The most important of these factors though is the user’s specified consistency level.&lt;/p&gt;

&lt;p&gt;SpiceDB allows you to choose amongst four levels of consistency on a per-request basis:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fully_consistent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chooses the system’s current time as the snapshot timestamp, and will include all changes that were fully committed before the request was started.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;snapshot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Evaluates requests at the exact same timestamp as that encoded in a ZedToken from a previous request.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;minimize_latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Allows the system to freely choose a timestamp that balances freshness while also minimizing latency.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;at_least_as_fresh&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Allows the system to choose a timestamp, as long as the time selected occurs after the time represented in the user-supplied ZedToken.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Fully Consistent
&lt;/h3&gt;

&lt;p&gt;At consistency level &lt;code&gt;fully_consistent&lt;/code&gt; we are always picking the exact snapshot timestamp at which the request arrived at the system. Here is an example with many requests and the snapshot timestamp chosen.&lt;/p&gt;



&lt;p&gt;As you can see, no two requests are evaluated at the same snapshot timestamp, effectively disabling the cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  At Exact Snapshot
&lt;/h3&gt;

&lt;p&gt;At consistency level &lt;code&gt;at_exact_snapshot&lt;/code&gt; we are always picking the &lt;em&gt;same&lt;/em&gt; snapshot revision for every request. This will result in perfect cache usage, but our results will get more and more stale as time goes on.&lt;/p&gt;



&lt;p&gt;In this example, the default setting for the Snapshot Revision is 4.25s and all of our requests are evaluated at that time. By the time we get to time 10 our results are already 5.75 seconds old. You can further adjust the Snapshot Revision setting and observe how the average staleness of results changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimize Latency
&lt;/h3&gt;

&lt;p&gt;At consistency level &lt;code&gt;minimize_latency&lt;/code&gt; we allow the server to pick a snapshot timestamp freely to optimize the experience. We use &lt;em&gt;quantization&lt;/em&gt;, a method to downsample high-frequency data into discrete buckets, to allow different servers to all choose the same request timestamps independently. In this example, the server is configured with &lt;code&gt;--datastore-revision-quantization-interval=1s&lt;/code&gt; (the default is 5s) and &lt;code&gt;--datastore-revision-quantization-max-staleness-percent=0&lt;/code&gt;.&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhiywnupfkkdwt5ssg2vb.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhiywnupfkkdwt5ssg2vb.png" alt="minimize_latency" width="734" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the request times are quantized (i.e. rounded) down to the nearest whole second. This lets us re-use cache entries for as many requests are we receive during that 1s window, and our results will be up to 1s stale. This allows us to make an intentional tradeoff between consistency and cache usage.&lt;/p&gt;

&lt;p&gt;In order to avoid effectively completely evicting the cache every time a new quantization period starts, &lt;a href="https://github.com/authzed/spicedb/pull/1285" rel="noopener noreferrer"&gt;Pull Request #1285&lt;/a&gt; introduced the ability to blend between one quantized revision and the next over a period of allowed staleness. The following example shows a staleness of 100% of the quantized revision, i.e. 1s:&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm48r8t6tdznhvqlwili9.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm48r8t6tdznhvqlwili9.png" alt="minimize_latency with 1s max staleness" width="734" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, there are multiple active candidate snapshots at every request time. Older snapshot revisions are phased out and newer snapshot revisions are phased in. We can visualize this probability as a stacked area chart, where all probabilities always add up to 100%. Here is such a chart for 1s max staleness:&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ia7e50kgarrmcycsm03.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ia7e50kgarrmcycsm03.png" alt="Snapshot Probability - 1s max_stale" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can adjust the staleness time according to our requirements for freshness of answers, even beyond 100%. The following graphs demonstrate 10% (0.1s) and 200% (2s) of max staleness respectively:&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funfd0ms5647t4kwii3nb.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funfd0ms5647t4kwii3nb.png" alt="Snapshot Probability - 0.1s max_stale.png" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl30xu4azamgawsyjh5qg.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl30xu4azamgawsyjh5qg.png" alt="Snapshot Probability - 2s max_stale.png" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Play with the Quantization Window and Max Stale Percentage settings to see how they affect snapshot revision reuse and result staleness.&lt;/p&gt;



&lt;h3&gt;
  
  
  At Least as Fresh
&lt;/h3&gt;

&lt;p&gt;At consistency level &lt;code&gt;at_least_as_fresh&lt;/code&gt; we use &lt;a href="https://authzed.com/docs/reference/zedtokens-and-zookies" rel="noopener noreferrer"&gt;ZedTokens&lt;/a&gt; (Zookies in the Zanzibar paper) to give the user a powerful way to express causality between requests in the system. Functionally, you can think of an &lt;code&gt;at_least_as_fresh&lt;/code&gt; request as being the same as &lt;code&gt;minimize_latency&lt;/code&gt; unless the selected timestamp is less than the timestamp encoded in the ZedToken. When that happens, the chosen timestamp is adjusted forward to use the timestamp encoded in the ZedToken. In our scatterplot visualizations this looks like the following:&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwlvz0qbhcyhyxezrpes.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwlvz0qbhcyhyxezrpes.png" alt="at_least_as_fresh.png" width="734" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see the familiar stepped pattern from &lt;code&gt;minimize_latency&lt;/code&gt;. However, when a request is received in between the ZedToken being issued and the next quantized revision, it is adjusted as seen in the following zoomed scatterplot when a request comes in between times 6.3 and 7 with the ZedToken at time 6.3:&lt;/p&gt;

&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%2Fauthzed.com%2Fimages%2Fblogs%2Fhotspot-caching-in-google-zanzibar-and-spicedb%2Fat_least_as_fresh_%282%29.png" 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%2Fauthzed.com%2Fimages%2Fblogs%2Fhotspot-caching-in-google-zanzibar-and-spicedb%2Fat_least_as_fresh_%282%29.png" alt="at_least_as_fresh (2).png" width="734" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adjust the settings in the following interactive chart and observe the effect on the timestamps.&lt;/p&gt;



&lt;p&gt;Allowing a user to pick their consistency level means that SpiceDB is usually picking revisions using all of these modes at the same time, with each request making the appropriate tradeoff between consistency, freshness, and performance.&lt;/p&gt;

&lt;p&gt;Now that we see how the system as a whole creates and utilizes immutable cache entries, let’s see how we scale it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistent Hashring Dispatch
&lt;/h2&gt;

&lt;p&gt;The Zanzibar paper Section 3.2.5 (that we referenced earlier) has this to say about scaling this caching solution:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Zanzibar servers in each cluster form a distributed cache for both reads and check evaluations, including intermediate check results evaluated during pointer chasing. Cache entries are distributed across Zanzibar servers with consistent hashing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Evan, one of our engineers, has written about the “how” for consistent hash load balancing before in his post: &lt;a href="https://authzed.com/blog/consistent-hash-load-balancing-grpc" rel="noopener noreferrer"&gt;Consistent Hash Load Balancing for gRPC&lt;/a&gt;, but I would like to expand a little bit more on the “why”.&lt;/p&gt;

&lt;p&gt;It’s simple math really: if all nodes were equally likely to compute (and cache) the results for every problem and subproblem, we would end up storing several copies of the same result. If our average cache result size is &lt;code&gt;B&lt;/code&gt; bytes, and we’ve dedicated &lt;code&gt;M&lt;/code&gt; bytes of memory on each server to caching, the total number of unique problems we can keep in cache (regardless of cluster size) is &lt;code&gt;M / B&lt;/code&gt;. Additionally, our cache hit ratio will suffer due to needing to recompute each cache entry on each server the first time that problem or sub-problem is encountered.&lt;/p&gt;

&lt;p&gt;If we were to always send the same problem or sub-problem to the same SpiceDB server or group of servers, we could achieve higher hit rates, and more overall caching. If we sent each problem to only one server, our overall number of unique cached items in a cluster of size &lt;code&gt;N&lt;/code&gt; would be &lt;code&gt;N * M / B&lt;/code&gt;. Thus, as our dataset grows, we can keep more of it in cache by expanding our cluster size correspondingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hotspot Caching vs. Other Types of Caching
&lt;/h2&gt;

&lt;p&gt;The type of caching we’ve discussed in this post, hotspot caching, is used to address latency problems when the &lt;em&gt;same&lt;/em&gt; data is accessed &lt;em&gt;frequently&lt;/em&gt;. There are other types of caching referenced in the Zanzibar paper as well. Section 3.2.5 also describes a &lt;a href="https://zanzibar.tech/2TA6DjUKDn:w.PRkCM6sny:-" rel="noopener noreferrer"&gt;relationship cache&lt;/a&gt; where groups of frequently accessed relationships are cached, and &lt;a href="https://zanzibar.tech/2IoYDUFMAE:0:T" rel="noopener noreferrer"&gt;Section 3.2.4&lt;/a&gt; describes the “Leopard Indexing System”, which is a continually updated denormalization of user group data stored in Zanzibar. Lovingly following the CPU cache naming model, we refer to these techniques internally as L0 and L2 caches, and have &lt;a href="https://github.com/authzed/spicedb/issues/207" rel="noopener noreferrer"&gt;designs&lt;/a&gt; for how these will make their way into SpiceDB in the future.&lt;/p&gt;

&lt;p&gt;But that’s a story for another time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sincerely, Your Most Faithful Implementation
&lt;/h2&gt;

&lt;p&gt;I hope this has been an informative post, and that you’ve learned a lot about our implementation of hotspot caching in SpiceDB. If you’re using one of AuthZed’s hosted SpiceDB solutions, you’re already taking advantage of hotspot caching automatically. If you’re one of our self-hosted or open source customers, I hope you’ve learned enough about how the various datastore parameters and consistency levels work together to tailor your experience. If you’d like to learn more, you can reach us through our &lt;a href="https://authzed.com/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;, by &lt;a href="https://authzed.com/call" rel="noopener noreferrer"&gt;scheduling a call&lt;/a&gt;, or by reaching out to &lt;a href="//mailto:support@authzed.com"&gt;support@authzed.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zanzibar.tech" rel="noopener noreferrer"&gt;AuthZed’s Annotated Google Zanzibar Paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://authzed.com/blog/consistent-hash-load-balancing-grpc" rel="noopener noreferrer"&gt;Consistent Hash Load Balancing for gRPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://authzed.com/blog/spicedb-architecture" rel="noopener noreferrer"&gt;The Architecture of SpiceDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://authzed.com/blog/what-is-google-zanzibar" rel="noopener noreferrer"&gt;What is Google Zanzibar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Image Credit: &lt;a href="https://commons.wikimedia.org/wiki/File:Magnificent_CME_Erupts_on_the_Sun_-_August_31.jpg" rel="noopener noreferrer"&gt;NASA Goddard Space Flight Center&lt;/a&gt;, &lt;a href="https://creativecommons.org/licenses/by/2.0" rel="noopener noreferrer"&gt;CC BY 2.0&lt;/a&gt;, via Wikimedia Commons&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
