<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roee Hershko</title>
    <description>The latest articles on DEV Community by Roee Hershko (@roee_hershko_bc6f44186f8e).</description>
    <link>https://dev.to/roee_hershko_bc6f44186f8e</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2442109%2Fc1452b3c-5d4b-4b2f-ad65-c04eae6a6581.png</url>
      <title>DEV Community: Roee Hershko</title>
      <link>https://dev.to/roee_hershko_bc6f44186f8e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/roee_hershko_bc6f44186f8e"/>
    <language>en</language>
    <item>
      <title>Your AI agent has more permissions than your users</title>
      <dc:creator>Roee Hershko</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:54:34 +0000</pubDate>
      <link>https://dev.to/roee_hershko_bc6f44186f8e/your-ai-agent-has-more-permissions-than-your-users-50in</link>
      <guid>https://dev.to/roee_hershko_bc6f44186f8e/your-ai-agent-has-more-permissions-than-your-users-50in</guid>
      <description>&lt;p&gt;Here is a conversation that happens in a lot of companies right now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dana:&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/assistant"&gt;@assistant&lt;/a&gt; please close PAY-123 and delete the old release branch&lt;br&gt;
&lt;strong&gt;Assistant:&lt;/strong&gt; Done ✅&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem: Dana is not allowed to delete branches in that repository. The assistant is.&lt;/p&gt;

&lt;p&gt;Most AI agents and chat bots act in other systems (Jira, GitHub, Slack, Salesforce, AWS) through &lt;strong&gt;one service account&lt;/strong&gt;. That account needs enough access to help everyone, so it ends up with more access than any single person who talks to it. Whatever the agent can do, anyone who can reach the agent can do too.&lt;/p&gt;

&lt;p&gt;This isn't a new problem. ChatOps bots have had it for years. But agents make it much worse: they take free-form requests, they chain tool calls on their own, and they can be talked into things.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Just tell the model not to"
&lt;/h2&gt;

&lt;p&gt;The first fix most teams try is the prompt: &lt;em&gt;"Only perform actions the user is authorized for."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That doesn't work, for a simple reason: &lt;strong&gt;the model doesn't know what Dana is allowed to do&lt;/strong&gt;, and the tool call runs with the bot's credential whatever the model believes. A prompt is a suggestion. Authorization has to happen outside the model, in code, before the tool runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The usual fixes, and where they fall short
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Per-user OAuth.&lt;/strong&gt; The agent acts with Dana's own token, so the system enforces Dana's permissions. When it's available and practical, this is the cleanest answer and you should use it. In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;many systems have no simple "the bot acts as Dana" flow (Kubernetes RBAC, AWS IAM, Argo CD, and a lot of internal tooling);&lt;/li&gt;
&lt;li&gt;every user has to go through a consent screen for every system before the agent is useful to them;&lt;/li&gt;
&lt;li&gt;the agent now stores refresh tokens for everyone, which is a much bigger prize for an attacker than one read-only credential.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Your own policy engine.&lt;/strong&gt; Copy each system's permission model into OPA, Cedar or a config file, and check against that. It works on day one. After that, every project role, repository team, Jira permission scheme and IAM policy change has to be mirrored, and the copy quietly drifts from reality. A permission check that is wrong in the permissive direction is worse than none, because people trust it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask the system that already knows
&lt;/h2&gt;

&lt;p&gt;Every one of these systems already knows exactly what Dana may do. Most of them can even say so for a named user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes has &lt;code&gt;SubjectAccessReview&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Jira has a permissions API that answers per user&lt;/li&gt;
&lt;li&gt;AWS can simulate a principal's IAM policies&lt;/li&gt;
&lt;li&gt;GitHub reports a user's effective role on a repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of copying the rules, the agent can &lt;strong&gt;ask before it acts&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;May &lt;a href="mailto:dana@example.com"&gt;dana@example.com&lt;/a&gt; do &lt;code&gt;DELETE_ISSUES&lt;/code&gt; on &lt;code&gt;issue:PAY-123&lt;/code&gt; in &lt;code&gt;jira-main&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's what I built &lt;a href="https://github.com/roee-hersh/hallpass" rel="noopener noreferrer"&gt;hallpass&lt;/a&gt; to do. It's a small, self-hosted service with one endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl localhost:8080/check &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "user": "dana@example.com",
  "connection": "jira-main",
  "action": "DELETE_ISSUES",
  "resource": "issue:PAY-123"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"denied: ..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;hallpass asks Jira, live, with its own &lt;strong&gt;read-only&lt;/strong&gt; credential. It never performs the action; it only answers the question. The agent keeps its own credential and does the work, but only after the check says &lt;code&gt;allow&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three answers, not two
&lt;/h2&gt;

&lt;p&gt;The part I care most about is that hallpass has three answers: &lt;code&gt;allow&lt;/code&gt;, &lt;code&gt;deny&lt;/code&gt; and &lt;strong&gt;&lt;code&gt;unknown&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deny&lt;/code&gt; means the system positively said no. &lt;code&gt;unknown&lt;/code&gt; means hallpass could not evaluate the question: the upstream timed out or rate-limited, hallpass's own credential was rejected, the resource isn't visible to it, or the policy uses a construct hallpass doesn't understand (an IAM condition, say). In all of those cases it doesn't guess, and callers should treat &lt;code&gt;unknown&lt;/code&gt; as deny.&lt;/p&gt;

&lt;p&gt;It sounds like a small detail, but it's the difference between a check you can trust and one that silently says yes when something breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like in an agent
&lt;/h2&gt;

&lt;p&gt;The check belongs in the tool wrapper, not the prompt. A minimal Python version:&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;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;HALLPASS&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;HALLPASS_URL&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;http://localhost:8080&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HALLPASS_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;def&lt;/span&gt; &lt;span class="nf"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HALLPASS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;headers&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;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&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;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY&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;json&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;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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;connection&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resource&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&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                      &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Anything other than an explicit allow, including "unknown" and errors, is a no.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;body&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;decision&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&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;reason&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="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_issue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requesting_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requesting_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jira-main&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;DELETE_ISSUES&lt;/span&gt;&lt;span class="sh"&gt;"&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;issue:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;issue_key&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Sorry, you&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;re not allowed to delete &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;issue_key&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;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;).&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# call Jira with the bot's credential
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is where &lt;code&gt;requesting_user&lt;/code&gt; comes from: the authenticated identity of whoever sent the message (the Slack user, the SSO session), never something the model wrote.&lt;/p&gt;

&lt;p&gt;If you use Strands, LangChain, LangGraph or the Claude Agent SDK, the repo ships a &lt;code&gt;@guarded&lt;/code&gt; decorator that does the same in one line on top of the framework's &lt;code&gt;@tool&lt;/code&gt;, with the user bound from your session so the tool schema never exposes a &lt;code&gt;user&lt;/code&gt; field:&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="nd"&gt;@tool&lt;/span&gt;
&lt;span class="nd"&gt;@guarded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jira-main&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;DELETE_ISSUES&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;issue:{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;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_user&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;delete_issue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What it covers today
&lt;/h2&gt;

&lt;p&gt;hallpass currently speaks to 21 systems: Jira, Confluence, GitHub, GitLab, Bitbucket, Slack, Google Workspace, Google Cloud, Microsoft 365, Azure, AWS, Kubernetes, Argo CD, Salesforce, Datadog, PagerDuty, Zendesk, Linear, Databricks, Snowflake and Vault. Each one is documented with the read-only credential it needs and what it cannot see.&lt;/p&gt;

&lt;p&gt;It's a single Go binary with one YAML file and no database. Secrets are only ever &lt;code&gt;env:&lt;/code&gt; or &lt;code&gt;file:&lt;/code&gt; references. Every integration is tested against a fake of its API that validates each request against the vendor's published OpenAPI description, and every resource parser is fuzzed nightly. (The fuzzer earned its keep this week: it found a Unicode control character slipping through a check that only rejected ASCII ones.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits worth knowing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's a check, not a transaction.&lt;/strong&gt; Permissions can change between the check and the action. Answers are cached for 30 seconds by default; set it to 0 if that matters to you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It trusts who the caller says the user is.&lt;/strong&gt; hallpass answers "may &lt;em&gt;this&lt;/em&gt; user…"; making sure the user really is Dana is your agent's job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It only knows what the system exposes.&lt;/strong&gt; If a permission can't be read with a read-only credential, the answer is &lt;code&gt;unknown&lt;/code&gt;, and the docs for each integration say exactly what it can't see.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sO&lt;/span&gt; https://raw.githubusercontent.com/roee-hersh/hallpass/main/examples/hallpass.yaml
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;HALLPASS_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;change-me &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/hallpass.yaml:/etc/hallpass/hallpass.yaml:ro"&lt;/span&gt; ghcr.io/roee-hersh/hallpass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example config has a fake integration, so you can see allow and deny answers in a minute without connecting anything real.&lt;/p&gt;

&lt;p&gt;The code is on GitHub under Apache 2.0: &lt;strong&gt;&lt;a href="https://github.com/roee-hersh/hallpass" rel="noopener noreferrer"&gt;https://github.com/roee-hersh/hallpass&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear how you handle this today. Per-user OAuth everywhere? Separate bots per team? Human approval for anything destructive? And which system should hallpass support next?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
