<?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>credgate - R posts</title><description>Posts about the credgate R package: detecting and protecting Indian personal data in R.</description><link>https://dwarpal.github.io/credgate/</link><atom:link href="https://dwarpal.github.io/credgate/feed_rss_created_r.xml" rel="self" type="application/rss+xml" /> <docs>https://github.com/dwarpal/credgate</docs><language>en</language> <pubDate>Tue, 01 Jan 1980 00:00:00 -0000</pubDate> <lastBuildDate>Tue, 01 Jan 1980 00:00:00 -0000</lastBuildDate> <ttl>1440</ttl> <generator>MkDocs RSS plugin - v1.17.9</generator> <image> <url>None</url> <title>credgate - R posts</title> <link>https://dwarpal.github.io/credgate/</link> </image> <item> <title>Redacting Indian personal data in R before it reaches an LLM</title> <author>Bharat Sikka</author> <author>Aishwarya Bais</author> <category>Detection</category> <category>R</category> <description>&lt;h1 id=&#34;redacting-indian-personal-data-in-r-before-it-reaches-an-llm&#34;&gt;Redacting Indian personal data in R before it reaches an LLM&lt;a class=&#34;headerlink&#34; href=&#34;#redacting-indian-personal-data-in-r-before-it-reaches-an-llm&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h1&gt; &lt;p&gt;R users are wiring language models into their work quickly: summarising call notes with &lt;a href=&#34;https://ellmer.tidyverse.org&#34;&gt;ellmer&lt;/a&gt;, classifying tickets, drafting replies. In a bank, an insurer or a hospital those notes are full of Aadhaar numbers, PANs and mobile numbers, and the DPDP Act, RBI guidance and plain caution all say they should not go to a model that does not need them - often not even to one running in-house on a shared server.&lt;/p&gt; &lt;p&gt;&lt;a href=&#34;https://github.com/dwarpal/credgate&#34;&gt;credgate&lt;/a&gt; is an R package (with a Python twin) that finds Indian personal data and takes it out before the prompt leaves your session. This post shows the R side.&lt;/p&gt; &lt;!-- more --&gt; &lt;h2 id=&#34;find-it&#34;&gt;Find it&lt;a class=&#34;headerlink&#34; href=&#34;#find-it&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# install.packages(&amp;quot;credgate&amp;quot;, repos = c(&amp;quot;https://dwarpal.r-universe.dev&amp;quot;, &amp;quot;https://cloud.r-project.org&amp;quot;))&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;library&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;credgate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;note&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;Customer name: Priya Nair. PAN ABCPN1234K, mobile +91 98765 43210, wants the EMI date moved.&amp;quot;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;detect_pii&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;note&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 3 PII matches (values masked; use `x$value` to see them):&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; doc start end type value confidence&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 16 25 person_name XXXXX XXXX 0.80&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 32 41 pan XXXXXX234K 0.97&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 51 65 mobile_in +XX XXXXX X3210 0.97&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Every match has a confidence and, if you ask, the reasons for it. These are not just patterns: an Aadhaar number must pass its Verhoeff checksum, a card number its Luhn check, a GSTIN its check character, so a mistyped or made-up number is not reported. Words nearby raise or lower the confidence: &#34;PAN&#34; next to a PAN-shaped value makes it near certain, &#34;order no&#34; next to a 12-digit number makes it unlikely to be an Aadhaar. Values are masked whenever credgate prints them, so a console log or a knitted report does not leak them either.&lt;/p&gt; &lt;p&gt;It reads Indian text as it is written, Devanagari digits included:&lt;/p&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nf&#34;&gt;detect_pii&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;आधार: ५४२२ २९३७ ११८२, मोबाइल 9468451273&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 2 PII matches (values masked; use `x$value` to see them):&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; doc start end type value confidence&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 7 20 aadhaar XXXX XXXX ११८२ 0.97&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 30 39 mobile_in XXXXXX1273 0.93&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&#34;look-before-you-send&#34;&gt;Look before you send&lt;a class=&#34;headerlink&#34; href=&#34;#look-before-you-send&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;&lt;code&gt;preview()&lt;/code&gt; shows exactly what would be replaced, and why:&lt;/p&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nf&#34;&gt;preview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;note&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;min_confidence&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 3 items would be redacted:&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; Customer name: [PERSON_NAME]. PAN [PAN], mobile [MOBILE_IN], wants the EMI date moved.&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; placeholder value confidence reasons&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [PERSON_NAME] XXXXX XXXX 0.80 matched Person name pattern&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [PAN] XXXXXX234K 0.97 matched PAN pattern; context &amp;#39;PAN&amp;#39;; holder type: individual&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [MOBILE_IN] +XX XXXXX X3210 0.97 matched Indian mobile number pattern; context &amp;#39;mob&amp;#39;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&#34;redact-then-ask-the-model&#34;&gt;Redact, then ask the model&lt;a class=&#34;headerlink&#34; href=&#34;#redact-then-ask-the-model&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nf&#34;&gt;library&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ellmer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;safe&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;redact&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;note&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;min_confidence&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;safe&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [1] &amp;quot;Customer name: [PERSON_NAME]. PAN [PAN], mobile [MOBILE_IN], wants the EMI date moved.&amp;quot;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;chat_ollama&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;model&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;gemma3:1b&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;chat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;paste&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;Summarise in one line:&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The model only ever sees &lt;code&gt;[PAN]&lt;/code&gt; and &lt;code&gt;[MOBILE_IN]&lt;/code&gt;. The same works with &lt;code&gt;chat_openai()&lt;/code&gt;, &lt;code&gt;chat_anthropic()&lt;/code&gt;, &lt;code&gt;chat_vllm()&lt;/code&gt; or any other provider: credgate runs before the request, in your R session, and makes no network calls of its own.&lt;/p&gt; &lt;h2 id=&#34;need-the-real-values-back-tokenise&#34;&gt;Need the real values back? Tokenise&lt;a class=&#34;headerlink&#34; href=&#34;#need-the-real-values-back-tokenise&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Redaction is one-way. When the answer should contain the real values - a drafted reply to the customer, say - tokenise instead: the model sees numbered tokens, and &lt;code&gt;restore()&lt;/code&gt; puts the values back into its answer.&lt;/p&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;vault&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;cg_vault&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;tokenise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;note&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vault&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;min_confidence&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [1] &amp;quot;Customer name: [PERSON_NAME_1]. PAN [PAN_1], mobile [MOBILE_IN_1], wants the EMI date moved.&amp;quot;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;answer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;Dear [PERSON_NAME_1], your request to move the EMI date for PAN [PAN_1] is noted.&amp;quot;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;restore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;answer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vault&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [1] &amp;quot;Dear Priya Nair, your request to move the EMI date for PAN ABCPN1234K is noted.&amp;quot;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The vault stays in your session; &lt;code&gt;vault_save()&lt;/code&gt; writes it encrypted with a passphrase if you need it later.&lt;/p&gt; &lt;h2 id=&#34;data-frames&#34;&gt;Data frames&lt;a class=&#34;headerlink&#34; href=&#34;#data-frames&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Before a table goes into a prompt, a RAG index or a shared file, find out which columns hold personal data:&lt;/p&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;calls&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;data.frame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cust_pan&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;ABCPE1234F&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;AAACR5055K&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;XYZPQ9876R&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;BNZPM2501F&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;notes&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;Called, Aadhaar 2345 6789 0124&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;no issues&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;mobile 9876543210&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;asked about EMI&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;scan_pii&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;calls&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; Scanned 3 columns; PII found in 2: cust_pan and notes&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; column type rate matched whole confidence example&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; cust_pan pan 100% 4 of 4 4 0.97 XXXXXX234F&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; notes aadhaar 25% 1 of 4 0 0.97 XXXX XXXX 0124&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; notes mobile_in 25% 1 of 4 0 0.93 XXXXXX3210&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;For tables too big for memory, the same functions work on Arrow datasets, Parquet files, DuckDB and other databases through DBI, and Spark through sparklyr, sampling where the data lives. See &lt;a href=&#34;../../large-data/&#34;&gt;large data&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&#34;what-it-does-not-do-yet&#34;&gt;What it does not do (yet)&lt;a class=&#34;headerlink&#34; href=&#34;#what-it-does-not-do-yet&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Names in running text.&lt;/strong&gt; credgate finds names after a title, label or relation (&#34;Smt.&#34;, &#34;Name:&#34;, &#34;S/o&#34;), not in a sentence like &#34;Delivery to Sandeep Pillai&#34;. If you know a column holds names, say so and every value is read as one; for notes, add your own name list or cues as a &lt;a href=&#34;../../custom-detectors/&#34;&gt;custom detector&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Evasion.&lt;/strong&gt; It handles accidental variation - Indic digits, hidden characters, mixed case - not someone deliberately spelling a PAN out letter by letter.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&#34;going-further&#34;&gt;Going further&lt;a class=&#34;headerlink&#34; href=&#34;#going-further&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Policies&lt;/strong&gt;: which models may see what, decided by InfoSec in one YAML file and applied with &lt;code&gt;policy_apply()&lt;/code&gt;; every decision can go to a tamper-evident audit log.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;A proxy&lt;/strong&gt; (from the Python package) applies the same policy to every request from Claude Code, Copilot Chat or ellmer pointed at it.&lt;/li&gt; &lt;li&gt;&lt;a href=&#34;../../how-it-works/&#34;&gt;How a match is scored&lt;/a&gt;, step by step.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;credgate is open source (Apache-2.0) and alpha: we are looking for people to try it and tell us what it misses. &lt;a href=&#34;https://github.com/dwarpal/credgate/issues/new?template=detection.yml&#34;&gt;Report a detection problem&lt;/a&gt; with a synthetic example, or start a &lt;a href=&#34;https://github.com/dwarpal/credgate/discussions&#34;&gt;discussion&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;All values in this post are synthetic.&lt;/p&gt;</description> <link>https://dwarpal.github.io/credgate/blog/redacting-indian-pii-in-r/</link> <pubDate>Fri, 06 Nov 2026 00:00:00 +0000</pubDate> <source url="https://dwarpal.github.io/credgate/feed_rss_created_r.xml">credgate - R posts</source><guid isPermaLink="true">https://dwarpal.github.io/credgate/blog/redacting-indian-pii-in-r/</guid> </item> <item> <title>A Shiny app for InfoSec: which models may see which data</title> <author>Bharat Sikka</author> <author>Aishwarya Bais</author> <category>Policy editor</category> <category>R</category> <description>&lt;h1 id=&#34;a-shiny-app-for-infosec-which-models-may-see-which-data&#34;&gt;A Shiny app for InfoSec: which models may see which data&lt;a class=&#34;headerlink&#34; href=&#34;#a-shiny-app-for-infosec-which-models-may-see-which-data&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h1&gt; &lt;p&gt;Once a bank lets analysts use language models, someone has to decide which models may see what. Customer PANs to the in-house model on the analyst&#39;s laptop: probably fine. To a shared GPU server: perhaps not. To an external provider: no. A card number with its CVV: never. That someone is usually InfoSec or compliance, and they should not have to edit YAML to say it.&lt;/p&gt; &lt;p&gt;&lt;a href=&#34;https://github.com/dwarpal/credgate&#34;&gt;credgate&lt;/a&gt; keeps these decisions in a &lt;strong&gt;policy&lt;/strong&gt;, and its R package includes a Shiny app to edit one. This post walks through it.&lt;/p&gt; &lt;!-- more --&gt; &lt;h2 id=&#34;the-policy&#34;&gt;The policy&lt;a class=&#34;headerlink&#34; href=&#34;#the-policy&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;A policy lists the model endpoints requests may go to, each with a tier - &lt;code&gt;local&lt;/code&gt; (the analyst&#39;s machine), &lt;code&gt;internal&lt;/code&gt; (models the organisation runs) or &lt;code&gt;frontier&lt;/code&gt; (external providers) - and rules, checked in order, that decide what happens to each identifier found: allow it, redact it, tokenise it, or block the whole request.&lt;/p&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nf&#34;&gt;library&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;credgate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;cg_policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;bank-default.yaml&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;policy_decide&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;Card 4111 1111 1111 1111, CVV 123, expiry 08/29&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;claude&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;allowed&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# FALSE&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blocked_by&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# &amp;quot;card-with-cvv-and-expiry&amp;quot;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Loading is strict: an unknown field, tier or identifier type is an error, never a silent &#34;allow&#34;.&lt;/p&gt; &lt;h2 id=&#34;the-editor&#34;&gt;The editor&lt;a class=&#34;headerlink&#34; href=&#34;#the-editor&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# install.packages(c(&amp;quot;shiny&amp;quot;, &amp;quot;bslib&amp;quot;))&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;policy_editor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;bank-default.yaml&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;img alt=&#34;Rules in the order they are checked, each with its conditions and action&#34; src=&#34;../../images/policy-editor/rules.png&#34; /&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Endpoints&lt;/strong&gt; and &lt;strong&gt;Rules&lt;/strong&gt;: add, change, reorder. The first rule that matches decides, so order matters; moving a rule shows straight away what changes.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Try it&lt;/strong&gt;: paste a synthetic sample, pick an endpoint, and see the decision on the unsaved policy - each identifier, its confidence, why it was found, the action and the rule that decided it. It also lists what looked like an identifier but was &lt;em&gt;not&lt;/em&gt; reported, and why (a failed checksum, a missing keyword, a confidence below the minimum).&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Tests&lt;/strong&gt;: once a decision is right, save it as a test. Every later change runs the tests first; a change that breaks one cannot be saved without saying why.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Detectors&lt;/strong&gt;: write and try custom detectors - loan account numbers, staff codes - with a form that generates the YAML.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Changes&lt;/strong&gt;: what differs from the saved file, and the change log.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;img alt=&#34;Try it: a card with its CVV and expiry is blocked for a frontier model&#34; src=&#34;../../images/policy-editor/try-blocked.png&#34; /&gt;&lt;/p&gt; &lt;h2 id=&#34;tests-the-behaviour-that-was-agreed&#34;&gt;Tests: the behaviour that was agreed&lt;a class=&#34;headerlink&#34; href=&#34;#tests-the-behaviour-that-was-agreed&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# bank.tests.yaml, beside the policy&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l l-Scalar l-Scalar-Plain&#34;&gt;credgate-policy-tests&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l l-Scalar l-Scalar-Plain&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;tests&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p p-Indicator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l l-Scalar l-Scalar-Plain&#34;&gt;card-blocked-for-claude&lt;/span&gt; &lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;endpoint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l l-Scalar l-Scalar-Plain&#34;&gt;claude&lt;/span&gt; &lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l l-Scalar l-Scalar-Plain&#34;&gt;Card 4111 1111 1111 1111, CVV 123, expiry 08/29&lt;/span&gt; &lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p p-Indicator&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;allowed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p p-Indicator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt; blocked_by&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p p-Indicator&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;card-with-cvv-and-expiry&lt;/span&gt;&lt;span class=&#34;p p-Indicator&#34;&gt;]}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nf&#34;&gt;run_policy_tests&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;bank.yaml&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;passed&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; id passed&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 card-blocked-for-claude TRUE&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The same tests run in CI with &lt;code&gt;credgate policy test&lt;/code&gt;, and the proxy refuses to load a new policy revision whose tests fail.&lt;/p&gt; &lt;h2 id=&#34;every-save-is-on-record&#34;&gt;Every save is on record&lt;a class=&#34;headerlink&#34; href=&#34;#every-save-is-on-record&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Saving raises the policy&#39;s revision and appends who saved it, when, why and what changed to a change log beside the policy. Each entry carries the hash of the one before, so an entry edited or removed afterwards is detected.&lt;/p&gt; &lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;yaml&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;read_yaml&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;bank.yaml&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destinations&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;copilot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blocked&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;FALSE&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;policy_diff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;yaml&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;read_yaml&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;bank.yaml&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [1] &amp;quot;endpoint copilot: blocked true -&amp;gt; false&amp;quot;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;policy_write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;bank.yaml&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;priya&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;note&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;CHG-1042 Copilot approved for code&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;policy_changes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;bank.yaml&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;revision&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;note&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; revision user note&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 2 priya CHG-1042 Copilot approved for code&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;quot;intact&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;#&amp;gt; [1] TRUE&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&#34;for-a-whole-team&#34;&gt;For a whole team&lt;a class=&#34;headerlink&#34; href=&#34;#for-a-whole-team&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;With a users file, the editor shows each person what their role allows, and refuses the rest on the server:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;admins&lt;/strong&gt; (InfoSec) change the policy;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;auditors&lt;/strong&gt; see everything and change nothing;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;users&lt;/strong&gt; (analysts) try the saved policy, see the tests and their own activity, and manage their own key for the credgate proxy - but not the policy&#39;s make-up.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Behind single sign-on (OAuth2 Proxy, Keycloak, a web server&#39;s SSO module), the signed-in name comes from a header: see the &lt;a href=&#34;../../deployment/&#34;&gt;deployment guide&lt;/a&gt;. The editor loads nothing from the internet, so it runs air-gapped.&lt;/p&gt; &lt;p&gt;The Python package has the same app (&lt;code&gt;credgate editor&lt;/code&gt;), on the same files, for teams without R.&lt;/p&gt; &lt;h2 id=&#34;try-it&#34;&gt;Try it&lt;a class=&#34;headerlink&#34; href=&#34;#try-it&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;credgate is open source (Apache-2.0) and alpha. If you look after AI governance or data protection, we would like to hear what is missing: open a &lt;a href=&#34;https://github.com/dwarpal/credgate/discussions&#34;&gt;discussion&lt;/a&gt; or an &lt;a href=&#34;https://github.com/dwarpal/credgate/issues/new/choose&#34;&gt;issue&lt;/a&gt;.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Install from &lt;a href=&#34;https://dwarpal.r-universe.dev/credgate&#34;&gt;R-universe&lt;/a&gt; (CRAN review in progress)&lt;/li&gt; &lt;li&gt;&lt;a href=&#34;../../policy-editor/&#34;&gt;The policy editor guide&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;All values in this post are synthetic.&lt;/p&gt;</description> <link>https://dwarpal.github.io/credgate/blog/shiny-policy-editor/</link> <pubDate>Fri, 06 Nov 2026 00:00:00 +0000</pubDate> <source url="https://dwarpal.github.io/credgate/feed_rss_created_r.xml">credgate - R posts</source><guid isPermaLink="true">https://dwarpal.github.io/credgate/blog/shiny-policy-editor/</guid> </item> </channel> </rss>