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%
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">❯</span><span class="g-ember">KHAELOR</span></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 <a href="/docs/cookbook.html">Cookbook</a>38 </div>39 <div class="group"><div class="group-title">Using KHAELOR</div>40 <a href="/docs/usage.html">The TUI</a>41 <a href="/docs/commands.html">Commands & keyboard</a>42 <a href="/docs/tools.html">Tools</a>43 <a href="/docs/sessions.html">Sessions</a>44 </div>45 <div class="group"><div class="group-title">Autonomy</div>46 <a href="/docs/phases.html">Phase gates</a>47 <a href="/docs/verification.html">Verification</a>48 <a href="/docs/worktrees.html">Parallel worktrees</a>49 <a href="/docs/daemon.html">The daemon</a>50 </div>51 <div class="group"><div class="group-title">Intelligence</div>52 <a href="/docs/repograph.html">Semantic index</a>53 <a href="/docs/memory.html">Project memory</a>54 <a href="/docs/context.html">Context engine</a>55 <a href="/docs/fork-replay.html">Fork · replay · sdiff</a>56 </div>57 <div class="group"><div class="group-title">Control</div>58 <a href="/docs/permissions.html">Permissions</a>59 <a href="/docs/configuration.html">Configuration</a>60 <a href="/docs/cli.html">CLI reference</a>61 </div>62 <div class="group"><div class="group-title">Internals</div>63 <a href="/docs/architecture.html">Architecture</a>64 <a href="/docs/events.html">The event model</a>65 <a href="/docs/theming.html">Theming & terminal</a>66 </div>67 <div class="group"><div class="group-title">Help</div>68 <a href="/docs/faq.html">FAQ & troubleshooting</a>69 </div>70</aside>7172<article class="content">7374<h1>Permissions</h1>75<p class="lead">Powerful but not annoying. Ordinary project work is friction-free; anything76destructive or unusually broad stays visible — and answering a prompt takes milliseconds.</p>7778<h2 id="capabilities">Capabilities, not tool names<a class="anchor" href="#capabilities">#</a></h2>79<p>KHAELOR evaluates every action against <strong>capabilities</strong> — what the action80actually does — rather than opaque tool names:</p>81<table>82 <tr><th>Capability</th><th>Meaning</th></tr>83 <tr><td><code>file.read</code></td><td class="wrap">Read file or directory content, anywhere</td></tr>84 <tr><td><code>file.write.project</code></td><td class="wrap">Create or modify a file <em>inside</em> the project</td></tr>85 <tr><td><code>file.write.outsideProject</code></td><td class="wrap">Create or modify a file <em>outside</em> the project root</td></tr>86 <tr><td><code>process.execute</code></td><td class="wrap">Run a foreground shell command</td></tr>87 <tr><td><code>process.background</code></td><td class="wrap">Start a long-lived background process</td></tr>88 <tr><td><code>network.access</code></td><td class="wrap">Command whose primary purpose is network I/O (<code>curl</code>, <code>ssh</code>, …)</td></tr>89 <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>, …)</td></tr>90</table>91<p>Paths are fully resolved (symlinks, <code>..</code>) <em>before</em> classification — a92write to <code>./x/../../etc/hosts</code> is correctly treated as outside the project. One93shell command can carry several capabilities at once (a <code>curl | git apply</code> pipeline94carries <code>process.execute</code> + <code>network.access</code> + <code>git.modify</code>);95<strong>deny beats ask beats allow</strong> across all of them.</p>9697<h2 id="actions">allow · ask · deny<a class="anchor" href="#actions">#</a></h2>98<p>Each rule maps a capability (and optionally a subject pattern — a command prefix or path99glob) to one of three actions:</p>100<ul>101 <li><code>allow</code> — runs silently.</li>102 <li><code>ask</code> — the inline panel appears; you decide.</li>103 <li><code>deny</code> — refused without a prompt; the agent is told why and steered to a104 different approach.</li>105</ul>106<p>Rules are evaluated <strong>last-match-wins</strong> across four layers, later layers107overriding earlier ones:</p>108<pre data-no-copy><code>built-in defaults → user (~/.khaelor/config.json) → project (.khaelor/config.json) → session grants</code></pre>109<p>Anything no rule mentions defaults to <code>ask</code> — the system fails closed.</p>110111<h2 id="panel">The permission panel<a class="anchor" href="#panel">#</a></h2>112<p>Never a bare <code>Allow? y/n</code>. The panel appears inline, already focused, and takes113three keys at most:</p>114<pre class="term" data-no-copy><code>╭─ KHAELOR requests permission ──────────────────────────╯115│ Run <span class="t-dim">process.execute</span> │116│ <span class="t-b">npm install</span> │117│ │118│ Working directory │119│ ~/dev/project │120│ │121│ <span class="t-warn">⚠ Installs packages (writes node_modules)</span> │122│ │123│ [ Enter ] Allow once │124│ [ A ] Always allow "npm install *" in project │125│ [ Esc ] Deny [ Tab ] Details │126╱─────────────────────────────────────────────────────╯</code></pre>127<table>128 <tr><th>Key</th><th>Effect</th></tr>129 <tr><td><kbd>Enter</kbd></td><td class="wrap"><strong>Allow once</strong> — covers exactly this one call, nothing wider.</td></tr>130 <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> ↔ <code>npm run *</code>) before you confirm.</td></tr>131 <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>132 <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>133</table>134<p>For file writes and edits, the panel body shows the <strong>actual diff</strong> that would135be applied, not just a path. For background processes it notes the process keeps running. While136a panel is open the status line reads <code>● Waiting for permission</code> — and there is137<strong>no auto-approval timeout</strong>: silence is never consent. A pending request even138survives quitting; it reappears when you resume the session.</p>139140<h2 id="always">How "always allow" patterns are generated<a class="anchor" href="#always">#</a></h2>141<p>The persisted pattern is derived from conservative parsing of the command, using a142per-tool prefix dictionary: <code>git push origin main</code> suggests143<code>git push *</code>; <code>npm run dev</code> suggests the exact <code>npm run dev</code>.144Crucially, KHAELOR <strong>refuses to generalize what it cannot fully read</strong>: commands145with shell operators (<code>&&</code>, <code>|</code>, <code>;</code>) or substitution146(<code>$(...)</code>, backticks) get <em>no</em> "always" option at all — only "allow once".147A standing grant is never wider than what the analyzer could verify.</p>148149<h2 id="defaults">The default policy<a class="anchor" href="#defaults">#</a></h2>150<p>Out of the box, KHAELOR is safe but not annoying:</p>151<table>152 <tr><th>Action</th><th>Default</th></tr>153 <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>154 <tr><td class="wrap">Writing inside the project</td><td class="wrap"><code>allow</code></td></tr>155 <tr><td class="wrap">Writing outside the project</td><td class="wrap"><code>ask</code></td></tr>156 <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>157 <tr><td class="wrap">Background processes</td><td class="wrap"><code>ask</code> (stdin to an already-approved process: <code>allow</code>)</td></tr>158 <tr><td class="wrap">Network commands</td><td class="wrap"><code>ask</code></td></tr>159 <tr><td class="wrap">Mutating git commands</td><td class="wrap"><code>ask</code></td></tr>160</table>161<p>The first-run experience teaches the loop once: approve <code>npm test</code> with162<kbd>A</kbd> and it never asks again in that project.</p>163164<h2 id="hardline">The hardline safety floor<a class="anchor" href="#hardline">#</a></h2>165<p>Beneath all configurable rules sits a short, built-in, <strong>non-overridable</strong> deny166list — checked against a de-obfuscated rendering of the command (quotes stripped,167<code>~</code>/<code>$HOME</code> expanded): <code>rm -rf /</code>, <code>rm -rf ~</code>,168<code>mkfs</code>, <code>dd</code> onto raw devices, fork bombs,169<code>chmod -R 777 /</code>, <code>shutdown</code>/<code>reboot</code>, force-pushes to170main/master when detectable, and similar catastrophes. No <code>allow</code> rule can permit171these. It is a floor against disaster, not the primary defense — the primary defense is the172ask-by-default policy above.</p>173174<h2 id="config">Configuring permissions<a class="anchor" href="#config">#</a></h2>175<p>Rules live in the <code>permissions</code> section of your config files176(<a href="/docs/configuration.html">Configuration</a> covers locations and precedence). Both a177shorthand and an explicit form are accepted:</p>178<pre><code>{179 "permissions": {180 "file.read": "allow",181182 "process.execute": {183 "git status": "allow",184 "git push *": "allow",185 "*": "ask"186 },187188 "rules": [189 { "capability": "file.write.outsideProject",190 "pattern": "/Users/me/notes/*",191 "action": "allow" }192 ]193 }194}</code></pre>195<ul>196 <li>Patterns use <code>*</code> wildcards; a pattern without <code>*</code> must match exactly.</li>197 <li>Order matters within a file (last match wins), and project rules override user rules.</li>198 <li>Grants made with <kbd>A</kbd> are appended to <code>.khaelor/config.json</code>199 atomically, immediately — they are ordinary rules on the next load and survive restarts by200 construction.</li>201 <li><code>/permissions</code> shows the full effective ruleset with the provenance of every202 rule, and supports deleting or reordering.</li>203</ul>204<div class="note">205<p>If a capability is denied wholesale (say <code>file.write.*: deny</code>), the affected206tools are removed from the model's tool list entirely — the agent never wastes a turn207requesting the impossible.</p>208</div>209210<h2 id="honesty">What permissions do — and don't — guarantee<a class="anchor" href="#honesty">#</a></h2>211<p>An honest limitation, stated plainly: permission enforcement gates <em>what the agent asks212to do</em>, not what an arbitrary binary does once running. <code>network.access</code>213detection is a UX signal, not an egress firewall. The mitigations are the ask-by-default214policy, the refusal to generalize unreadable commands, and the hardline floor. Every215permission decision — request, matched rule, grant scope, denial feedback — is a durable216event in the <a href="/docs/sessions.html#event-log">session log</a>, so you can always217reconstruct exactly why an action ran.</p>218219<div class="pager">220 <div><span class="label">Previous</span><a href="/docs/tools.html">← Tools</a></div>221 <div class="next"><span class="label">Next</span><a href="/docs/configuration.html">Configuration →</a></div>222</div>223224</article>225</div></main>226227<footer class="site-footer"><div class="inner">228 <span>KHAELOR — Simon-Pierre Boucher · <a href="mailto:contact@spboucher.ai">contact@spboucher.ai</a></span>229 <span>Anthropic-powered · terminal-native by design</span>230</div></footer>231232</body>233</html>234