SPB Git

spb/khaelor Public

KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.

TypeScript 82.9% HTML 14.9% CSS 1.1% JavaScript 0.7%
13.1 KB · 215 lines html
Raw Blame History
1<!doctype html>2<!--3KHAELOR4File: website/public/docs/permissions.html5Description: The permission system — capabilities, allow/ask/deny, the panel keys, persisted grants, config examples.6Author: Simon-Pierre Boucher7Contact: contact@spboucher.ai8-->9<html lang="en">10<head>11<meta charset="utf-8">12<meta name="viewport" content="width=device-width, initial-scale=1">13<title>Permissions — KHAELOR</title>14<meta name="description" content="KHAELOR's capability-based permission system: allow/ask/deny rules, the inline permission panel, and persisted grants.">15<link rel="stylesheet" href="/styles.css">16<script>17(function(){try{var t=localStorage.getItem("khaelor-theme");if(!t&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches)t="light";if(t==="light")document.documentElement.setAttribute("data-theme","light");}catch(e){}})();18</script>19<script defer src="/site.js"></script>20</head>21<body>2223<header class="site-header"><div class="inner">24  <a class="wordmark" href="/"><span class="glyph">&#10095;</span>KHAELOR</a>25  <nav>26    <a href="/docs/getting-started.html">Docs</a>27    <a href="/#install">Install</a>28    <button id="theme-toggle" type="button" aria-label="Toggle color theme">light</button>29  </nav>30</div></header>3132<main class="page"><div class="docs-layout">3334<aside class="sidebar">35  <div class="group"><div class="group-title">Start</div>36    <a href="/docs/getting-started.html">Getting started</a>37  </div>38  <div class="group"><div class="group-title">Using KHAELOR</div>39    <a href="/docs/usage.html">The TUI</a>40    <a href="/docs/commands.html">Commands &amp; keyboard</a>41    <a href="/docs/tools.html">Tools</a>42    <a href="/docs/sessions.html">Sessions</a>43  </div>44  <div class="group"><div class="group-title">Control</div>45    <a href="/docs/permissions.html">Permissions</a>46    <a href="/docs/configuration.html">Configuration</a>47  </div>48  <div class="group"><div class="group-title">Help</div>49    <a href="/docs/faq.html">FAQ &amp; troubleshooting</a>50  </div>51</aside>5253<article class="content">5455<h1>Permissions</h1>56<p class="lead">Powerful but not annoying. Ordinary project work is friction-free; anything57destructive or unusually broad stays visible — and answering a prompt takes milliseconds.</p>5859<h2 id="capabilities">Capabilities, not tool names<a class="anchor" href="#capabilities">#</a></h2>60<p>KHAELOR evaluates every action against <strong>capabilities</strong> — what the action61actually does — rather than opaque tool names:</p>62<table>63  <tr><th>Capability</th><th>Meaning</th></tr>64  <tr><td><code>file.read</code></td><td class="wrap">Read file or directory content, anywhere</td></tr>65  <tr><td><code>file.write.project</code></td><td class="wrap">Create or modify a file <em>inside</em> the project</td></tr>66  <tr><td><code>file.write.outsideProject</code></td><td class="wrap">Create or modify a file <em>outside</em> the project root</td></tr>67  <tr><td><code>process.execute</code></td><td class="wrap">Run a foreground shell command</td></tr>68  <tr><td><code>process.background</code></td><td class="wrap">Start a long-lived background process</td></tr>69  <tr><td><code>network.access</code></td><td class="wrap">Command whose primary purpose is network I/O (<code>curl</code>, <code>ssh</code>, &hellip;)</td></tr>70  <tr><td><code>git.modify</code></td><td class="wrap">Git command that mutates repository state (<code>commit</code>, <code>push</code>, <code>reset</code>, &hellip;)</td></tr>71</table>72<p>Paths are fully resolved (symlinks, <code>..</code>) <em>before</em> classification — a73write to <code>./x/../../etc/hosts</code> is correctly treated as outside the project. One74shell command can carry several capabilities at once (a <code>curl | git apply</code> pipeline75carries <code>process.execute</code> + <code>network.access</code> + <code>git.modify</code>);76<strong>deny beats ask beats allow</strong> across all of them.</p>7778<h2 id="actions">allow &middot; ask &middot; deny<a class="anchor" href="#actions">#</a></h2>79<p>Each rule maps a capability (and optionally a subject pattern — a command prefix or path80glob) to one of three actions:</p>81<ul>82  <li><code>allow</code> — runs silently.</li>83  <li><code>ask</code> — the inline panel appears; you decide.</li>84  <li><code>deny</code> — refused without a prompt; the agent is told why and steered to a85  different approach.</li>86</ul>87<p>Rules are evaluated <strong>last-match-wins</strong> across four layers, later layers88overriding earlier ones:</p>89<pre data-no-copy><code>built-in defaults  →  user (~/.khaelor/config.json)  →  project (.khaelor/config.json)  →  session grants</code></pre>90<p>Anything no rule mentions defaults to <code>ask</code> — the system fails closed.</p>9192<h2 id="panel">The permission panel<a class="anchor" href="#panel">#</a></h2>93<p>Never a bare <code>Allow? y/n</code>. The panel appears inline, already focused, and takes94three keys at most:</p>95<pre class="term" data-no-copy><code>&#9581;&#9472; KHAELOR requests permission &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9583;96&#9474;  Run                                <span class="t-dim">process.execute</span>  &#9474;97&#9474;  <span class="t-b">npm install</span>                                        &#9474;98&#9474;                                                     &#9474;99&#9474;  Working directory                                  &#9474;100&#9474;  ~/dev/project                                      &#9474;101&#9474;                                                     &#9474;102&#9474;  <span class="t-warn">&#9888; Installs packages (writes node_modules)</span>          &#9474;103&#9474;                                                     &#9474;104&#9474;  [ Enter ] Allow once                               &#9474;105&#9474;  [ A ]     Always allow "npm install *" in project  &#9474;106&#9474;  [ Esc ]   Deny            [ Tab ] Details          &#9474;107&#9585;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9583;</code></pre>108<table>109  <tr><th>Key</th><th>Effect</th></tr>110  <tr><td><kbd>Enter</kbd></td><td class="wrap"><strong>Allow once</strong> — covers exactly this one call, nothing wider.</td></tr>111  <tr><td><kbd>A</kbd></td><td class="wrap"><strong>Always allow in this project</strong> — persists the exact pattern shown (e.g. <code>npm install *</code>) to <code>.khaelor/config.json</code>. When several candidate patterns exist, <kbd>A</kbd> cycles specificity (<code>npm run dev</code> &harr; <code>npm run *</code>) before you confirm.</td></tr>112  <tr><td><kbd>Esc</kbd></td><td class="wrap"><strong>Deny</strong> — with an optional one-line reason that is handed to the agent as course correction (<em>"use pnpm in this repo"</em>), not a dead end.</td></tr>113  <tr><td><kbd>Tab</kbd></td><td class="wrap"><strong>Details</strong> — every capability request in the decision, the matched rule and its source, resolved absolute paths.</td></tr>114</table>115<p>For file writes and edits, the panel body shows the <strong>actual diff</strong> that would116be applied, not just a path. For background processes it notes the process keeps running. While117a panel is open the status line reads <code>&#9679; Waiting for permission</code> — and there is118<strong>no auto-approval timeout</strong>: silence is never consent. A pending request even119survives quitting; it reappears when you resume the session.</p>120121<h2 id="always">How "always allow" patterns are generated<a class="anchor" href="#always">#</a></h2>122<p>The persisted pattern is derived from conservative parsing of the command, using a123per-tool prefix dictionary: <code>git push origin main</code> suggests124<code>git push *</code>; <code>npm run dev</code> suggests the exact <code>npm run dev</code>.125Crucially, KHAELOR <strong>refuses to generalize what it cannot fully read</strong>: commands126with shell operators (<code>&amp;&amp;</code>, <code>|</code>, <code>;</code>) or substitution127(<code>$(...)</code>, backticks) get <em>no</em> "always" option at all — only "allow once".128A standing grant is never wider than what the analyzer could verify.</p>129130<h2 id="defaults">The default policy<a class="anchor" href="#defaults">#</a></h2>131<p>Out of the box, KHAELOR is safe but not annoying:</p>132<table>133  <tr><th>Action</th><th>Default</th></tr>134  <tr><td class="wrap">Reading files</td><td class="wrap"><code>allow</code> — except secret-shaped files (<code>*.env</code>, <code>*.pem</code>, <code>~/.ssh/*</code>), which <code>ask</code></td></tr>135  <tr><td class="wrap">Writing inside the project</td><td class="wrap"><code>allow</code></td></tr>136  <tr><td class="wrap">Writing outside the project</td><td class="wrap"><code>ask</code></td></tr>137  <tr><td class="wrap">Shell commands</td><td class="wrap"><code>ask</code> — with a read-only allowlist (<code>git status/diff/log/show</code>, <code>ls</code>, <code>cat</code>, <code>head</code>, <code>tail</code>, <code>which</code>, <code>pwd</code>, <code>wc</code>) so routine inspection never prompts</td></tr>138  <tr><td class="wrap">Background processes</td><td class="wrap"><code>ask</code> (stdin to an already-approved process: <code>allow</code>)</td></tr>139  <tr><td class="wrap">Network commands</td><td class="wrap"><code>ask</code></td></tr>140  <tr><td class="wrap">Mutating git commands</td><td class="wrap"><code>ask</code></td></tr>141</table>142<p>The first-run experience teaches the loop once: approve <code>npm test</code> with143<kbd>A</kbd> and it never asks again in that project.</p>144145<h2 id="hardline">The hardline safety floor<a class="anchor" href="#hardline">#</a></h2>146<p>Beneath all configurable rules sits a short, built-in, <strong>non-overridable</strong> deny147list — checked against a de-obfuscated rendering of the command (quotes stripped,148<code>~</code>/<code>$HOME</code> expanded): <code>rm -rf /</code>, <code>rm -rf ~</code>,149<code>mkfs</code>, <code>dd</code> onto raw devices, fork bombs,150<code>chmod -R 777 /</code>, <code>shutdown</code>/<code>reboot</code>, force-pushes to151main/master when detectable, and similar catastrophes. No <code>allow</code> rule can permit152these. It is a floor against disaster, not the primary defense — the primary defense is the153ask-by-default policy above.</p>154155<h2 id="config">Configuring permissions<a class="anchor" href="#config">#</a></h2>156<p>Rules live in the <code>permissions</code> section of your config files157(<a href="/docs/configuration.html">Configuration</a> covers locations and precedence). Both a158shorthand and an explicit form are accepted:</p>159<pre><code>{160  "permissions": {161    "file.read": "allow",162163    "process.execute": {164      "git status": "allow",165      "git push *": "allow",166      "*": "ask"167    },168169    "rules": [170      { "capability": "file.write.outsideProject",171        "pattern": "/Users/me/notes/*",172        "action": "allow" }173    ]174  }175}</code></pre>176<ul>177  <li>Patterns use <code>*</code> wildcards; a pattern without <code>*</code> must match exactly.</li>178  <li>Order matters within a file (last match wins), and project rules override user rules.</li>179  <li>Grants made with <kbd>A</kbd> are appended to <code>.khaelor/config.json</code>180  atomically, immediately — they are ordinary rules on the next load and survive restarts by181  construction.</li>182  <li><code>/permissions</code> shows the full effective ruleset with the provenance of every183  rule, and supports deleting or reordering.</li>184</ul>185<div class="note">186<p>If a capability is denied wholesale (say <code>file.write.*: deny</code>), the affected187tools are removed from the model's tool list entirely — the agent never wastes a turn188requesting the impossible.</p>189</div>190191<h2 id="honesty">What permissions do — and don't — guarantee<a class="anchor" href="#honesty">#</a></h2>192<p>An honest limitation, stated plainly: permission enforcement gates <em>what the agent asks193to do</em>, not what an arbitrary binary does once running. <code>network.access</code>194detection is a UX signal, not an egress firewall. The mitigations are the ask-by-default195policy, the refusal to generalize unreadable commands, and the hardline floor. Every196permission decision — request, matched rule, grant scope, denial feedback — is a durable197event in the <a href="/docs/sessions.html#event-log">session log</a>, so you can always198reconstruct exactly why an action ran.</p>199200<div class="pager">201  <div><span class="label">Previous</span><a href="/docs/tools.html">&larr; Tools</a></div>202  <div class="next"><span class="label">Next</span><a href="/docs/configuration.html">Configuration &rarr;</a></div>203</div>204205</article>206</div></main>207208<footer class="site-footer"><div class="inner">209  <span>KHAELOR — Simon-Pierre Boucher &middot; <a href="mailto:contact@spboucher.ai">contact@spboucher.ai</a></span>210  <span>Anthropic-powered &middot; terminal-native by design</span>211</div></footer>212213</body>214</html>215