<?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: Randy Cylonix</title>
    <description>The latest articles on DEV Community by Randy Cylonix (@dryricenoodle).</description>
    <link>https://dev.to/dryricenoodle</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%2F3984449%2Fe6a52391-c40a-42da-aef5-b882ba3e3456.jpg</url>
      <title>DEV Community: Randy Cylonix</title>
      <link>https://dev.to/dryricenoodle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dryricenoodle"/>
    <language>en</language>
    <item>
      <title>Why Not Give OpenClaw Root on the Production Server?</title>
      <dc:creator>Randy Cylonix</dc:creator>
      <pubDate>Tue, 30 Jun 2026 06:56:49 +0000</pubDate>
      <link>https://dev.to/dryricenoodle/why-not-give-openclaw-root-on-the-production-server-520o</link>
      <guid>https://dev.to/dryricenoodle/why-not-give-openclaw-root-on-the-production-server-520o</guid>
      <description>&lt;p&gt;It is a fair question, and it deserves a real answer instead of a nervous&lt;br&gt;
laugh. OpenClaw is good at real work: check whether a server is healthy,&lt;br&gt;
summarize the morning's email, keep an eye on a production cluster. The fastest&lt;br&gt;
way to let it do all of that is to hand it an SSH key and let it read your Mail.&lt;br&gt;
So why not?&lt;/p&gt;

&lt;p&gt;Here is the honest answer. A chat-driven agent with a root shell on production&lt;br&gt;
and an open door to your inbox is not one helpful feature. It is a&lt;br&gt;
general-purpose privileged path, and anyone who can talk to the agent can aim&lt;br&gt;
it. A clever prompt, a poisoned web page it reads, one confused tool call, and&lt;br&gt;
"summarize my inbox" quietly turns into "read everything and run whatever." The&lt;br&gt;
thing to be afraid of was never OpenClaw. It is everything that can reach&lt;br&gt;
OpenClaw.&lt;/p&gt;

&lt;p&gt;So the goal is not to trust the agent more. It is to need to trust it less. This&lt;br&gt;
post shows that shape on two everyday jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;monitor server status of &lt;code&gt;manage.cylonix.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;summarize the day's unread email&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both run through OpenScope, a capability broker that turns raw privileged access&lt;br&gt;
into scoped, auditable, policy-bound actions. OpenClaw never gets a root shell&lt;br&gt;
and never gets raw access to Mail. It gets a short list of approved actions, and&lt;br&gt;
nothing else.&lt;/p&gt;

&lt;p&gt;Everything below is a real run on a real machine. The command output is real,&lt;br&gt;
lightly condensed for readability.&lt;/p&gt;
&lt;h2&gt;
  
  
  The shape: broker in the middle
&lt;/h2&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OpenClaw -&amp;gt; ssh root@server         (full root shell)
OpenClaw -&amp;gt; osascript -&amp;gt; Apple Mail (full mailbox access)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OpenClaw -&amp;gt; openscope -&amp;gt; openscoped -&amp;gt; the one approved action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenScope holds the SSH key and the macOS automation approval. OpenClaw holds a&lt;br&gt;
low-permission label, &lt;code&gt;openclaw&lt;/code&gt;, and a list of actions that label is allowed&lt;br&gt;
to run. The broker checks policy on every call, runs the approved action, and&lt;br&gt;
writes the decision to an audit log.&lt;/p&gt;

&lt;p&gt;That changes the security model from:&lt;/p&gt;

&lt;p&gt;"watch the privileged path"&lt;/p&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;p&gt;"remove the privileged path from the agent."&lt;/p&gt;
&lt;h2&gt;
  
  
  Wiring OpenClaw to OpenScope
&lt;/h2&gt;

&lt;p&gt;OpenScope ships a small MCP server, &lt;code&gt;openscope-mcp&lt;/code&gt;. It holds no keys and no&lt;br&gt;
policy authority. It is a thin front end that forwards each call to the broker&lt;br&gt;
daemon and exposes only the actions the current agent is allowed to run.&lt;/p&gt;

&lt;p&gt;OpenClaw talks to MCP servers through mcporter, so registering OpenScope is one&lt;br&gt;
command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcporter config add openscope &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--command&lt;/span&gt; openscope-mcp &lt;span class="nt"&gt;--arg&lt;/span&gt; &lt;span class="nt"&gt;--agent&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; openclaw &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"OpenScope capability broker (agent openclaw)"&lt;/span&gt; &lt;span class="nt"&gt;--scope&lt;/span&gt; home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, OpenClaw can see exactly the actions policy allows for the&lt;br&gt;
&lt;code&gt;openclaw&lt;/code&gt; label, and no more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mcporter list openscope
openscope - OpenScope capability broker (agent openclaw)

  function mail_list_messages(limit?: number, unread?: string);
  function mail_read_message(id: string);
  function ssh_check_host();
  function ssh_host_metrics();
  function ssh_list_dir(path: string);
  function ssh_read_file(path: string);

  ... STDIO openscope-mcp --agent openclaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to notice. The mailbox is pinned to &lt;code&gt;Inbox&lt;/code&gt; by policy, so it is not&lt;br&gt;
even a parameter the agent can set. And there is no &lt;code&gt;restart_service&lt;/code&gt;, no&lt;br&gt;
&lt;code&gt;write_file&lt;/code&gt;, no shell. The tool list is generated live from policy, so it is&lt;br&gt;
also the honest list: if policy changes, the tools change, no restart required.&lt;/p&gt;

&lt;p&gt;The CLI form is the documented fallback, and it is what shows up in the audit&lt;br&gt;
log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openscope ssh host_metrics &lt;span class="nt"&gt;--agent&lt;/span&gt; openclaw &lt;span class="nt"&gt;--target&lt;/span&gt; cylonix-manage
openscope mail list_messages &lt;span class="nt"&gt;--agent&lt;/span&gt; openclaw &lt;span class="nt"&gt;--mailbox&lt;/span&gt; Inbox &lt;span class="nt"&gt;--unread&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, OpenClaw's workspace manual gets a short contract so the agent knows&lt;br&gt;
the rules: always act as &lt;code&gt;openclaw&lt;/code&gt;, route these jobs through OpenScope, treat&lt;br&gt;
a denial as final, never fall back to raw &lt;code&gt;ssh&lt;/code&gt; or &lt;code&gt;osascript&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Job 1: monitor manage.cylonix.io
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;manage.cylonix.io&lt;/code&gt; is the Cylonix control plane. It sits behind Cloudflare, so&lt;br&gt;
the broker target points at the origin IP directly rather than the proxy edge.&lt;br&gt;
There is no target for it yet, so the first step is to add one. This is the part&lt;br&gt;
worth slowing down for, because it is where the security actually lives.&lt;/p&gt;
&lt;h3&gt;
  
  
  The agent proposes, a human applies
&lt;/h3&gt;

&lt;p&gt;OpenClaw does not grant itself access. It drafts a proposal, a typed YAML file&lt;br&gt;
that says what it wants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ssh_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cylonix-manage&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;146.190.157.29&lt;/span&gt;            &lt;span class="c1"&gt;# origin IP; manage.cylonix.io is behind Cloudflare&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;identity_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/openscope/ssh/cylonix-manage/id_rsa&lt;/span&gt;
      &lt;span class="na"&gt;allowed_path_prefixes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/log&lt;/span&gt;
&lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;allow&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openclaw&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ssh&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;check_host&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;   &lt;span class="nv"&gt;constraints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cylonix-manage&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;allow&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openclaw&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ssh&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;host_metrics&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;constraints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cylonix-manage&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;allow&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openclaw&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ssh&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;list_dir&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;     &lt;span class="nv"&gt;constraints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cylonix-manage&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;allow&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openclaw&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ssh&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;read_file&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;    &lt;span class="nv"&gt;constraints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cylonix-manage&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;openscope plan&lt;/code&gt; reviews it. This step is read-only and needs no sudo. It&lt;br&gt;
renders exactly what the change grants, runs a lint, and checks the proposal&lt;br&gt;
against a root-owned envelope of bounds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHAT IT WILL BE ABLE TO DO (from typed fields)
  openclaw | ssh·check_host   | target=cylonix-manage
  openclaw | ssh·host_metrics | target=cylonix-manage
  openclaw | ssh·list_dir     | target=cylonix-manage
  openclaw | ssh·read_file    | target=cylonix-manage

FINDINGS
  HIGH   SSH-ROOT-USER   cylonix-manage   logs in as root
  MEDIUM SSH-BROAD-PREFIX cylonix-manage:/var/log  broad read prefix
  PASS   SSH-NO-BYPASS   ~/.ssh   no ~/.ssh key reaches the new target; verified live
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;SSH-NO-BYPASS&lt;/code&gt; line is doing quiet, important work. The plan checks, live,&lt;br&gt;
that none of your personal &lt;code&gt;~/.ssh&lt;/code&gt; keys can reach the new host. If one could,&lt;br&gt;
the broker boundary would be theater: the agent could just SSH around it. The&lt;br&gt;
key the broker uses is root-owned and unreadable to the agent.&lt;/p&gt;

&lt;p&gt;A human applies it, pins the hash, and acknowledges the root-login finding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;openscope apply &lt;span class="nt"&gt;--file&lt;/span&gt; dist/cylonix-manage.proposal.yaml &lt;span class="nt"&gt;--expect-hash&lt;/span&gt; 2466b42e77e8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The target and the rules now live in root-owned files. The same uid the agent&lt;br&gt;
runs as cannot edit them.&lt;/p&gt;
&lt;h3&gt;
  
  
  What the agent can now see
&lt;/h3&gt;

&lt;p&gt;The four verbs are read-only and immediately useful. Host identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openscope ssh check_host --agent openclaw --target cylonix-manage
{ "hostname": "manage-master-1", "os": "Linux", "release": "5.4.0-204-generic",
  "uid": "0", "user": "root" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node health, parsed into clean fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openscope ssh host_metrics --agent openclaw --target cylonix-manage
{ "uptime":  "22:40:43 up 449 days, 17:09,  load average: 2.63, 1.78, 1.30",
  "loadavg": "2.63 1.78 1.30",
  "memory":  "Mem: 3919 2458 123 126 1338 1064",
  "disk_root": "/dev/vda1  81106868 67962244 13128240  84% /" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This box is a Kubernetes node, so listing &lt;code&gt;/var/log/containers&lt;/code&gt; is effectively a&lt;br&gt;
live inventory of the Cylonix stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openscope ssh list_dir --agent openclaw --target cylonix-manage --path /var/log/containers
cylonix, cylonix-ui, supervisor, log-collector, ipdrawer,
postgres, redis, influxdb, etcd, keycloak, prometheus, traefik,
cilium, coredns, kube-apiserver, kube-scheduler, kube-controller-manager
(33 pod log files)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;449 days of uptime, moderate load, and a root filesystem at 84 percent. The&lt;br&gt;
agent has enough to write a useful status report, and it got there without a&lt;br&gt;
shell.&lt;/p&gt;
&lt;h3&gt;
  
  
  What the agent cannot do
&lt;/h3&gt;

&lt;p&gt;The containment is the point, so it is worth showing the walls.&lt;/p&gt;

&lt;p&gt;A verb that is not in the surface is denied at the policy layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openscope ssh service_status --agent openclaw --target cylonix-manage --service kubelet
no matching allow rule
(exit 3)

$ openscope ssh restart_service --agent openclaw --target cylonix-manage --service kubelet
no matching allow rule
(exit 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A path outside the allow-list is denied at the target layer, even though&lt;br&gt;
&lt;code&gt;read_file&lt;/code&gt; itself is allowed. The &lt;code&gt;/var/log&lt;/code&gt; fence is fail-closed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openscope ssh read_file --agent openclaw --target cylonix-manage --path /etc/passwd
path "/etc/passwd" is not allowed for target "cylonix-manage"
(exit 5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two independent gates, the policy rule and the target's allow-list, both have to&lt;br&gt;
say yes. The agent reads a node's health and its &lt;code&gt;/var/log&lt;/code&gt;. It cannot restart a&lt;br&gt;
service, cannot read &lt;code&gt;/etc&lt;/code&gt;, and cannot reach any other host.&lt;/p&gt;

&lt;p&gt;There is one more wall worth showing. Suppose the agent later decides it wants&lt;br&gt;
&lt;code&gt;service_status&lt;/code&gt; and tries to quietly widen the target's declared services.&lt;br&gt;
OpenScope refuses to mutate a live target in place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK  SSH-TARGET-CONFLICT  cylonix-manage
       alias already exists with different settings, apply will refuse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Widening an existing target is not a silent edit. It is a fresh, reviewed&lt;br&gt;
change. The agent that drafted the proposal cannot sneak a broader one past the&lt;br&gt;
plan gate.&lt;/p&gt;
&lt;h2&gt;
  
  
  Job 2: summarize the day's email
&lt;/h2&gt;

&lt;p&gt;Email is the second job, and it uses the same model with a different app.&lt;br&gt;
OpenScope brokers read-only access to Apple Mail. The &lt;code&gt;openclaw&lt;/code&gt; label is scoped&lt;br&gt;
to &lt;code&gt;Inbox&lt;/code&gt;, and an admin-owned sender-domain filter can narrow it further.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openscope mail list_messages --agent openclaw --mailbox Inbox --unread true --limit 20
[ { "sender": "The Google Workspace Team", "subject": "[Legal Update] Changes to Terms of Service", "unread": true },
  { "sender": "EZBLOCK INC via TestFlight", "subject": "Cylonix 1.0.12 (13) for iOS is now available to test", "unread": true },
  { "sender": "Microsoft Azure", "subject": "Make sure your Azure payment method is up to date", "unread": true },
  { "sender": "Google Search Console Team", "subject": "Improve Google presence for openscopeai.com", "unread": true },
  ... ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;read_message&lt;/code&gt; returns one message body, again &lt;code&gt;Inbox&lt;/code&gt;-only. There is no raw&lt;br&gt;
AppleScript anywhere in this path. The macOS automation approval stays on&lt;br&gt;
OpenScope's signed broker, not on the agent, so the agent never holds a general&lt;br&gt;
key to Mail. It asks for one message at a time, and policy decides.&lt;/p&gt;
&lt;h2&gt;
  
  
  OpenClaw, doing both jobs for real
&lt;/h2&gt;

&lt;p&gt;Here is the part that ties it together. OpenClaw was given one instruction:&lt;br&gt;
produce a daily ops briefing, server plus email, using OpenScope only. It made&lt;br&gt;
the four calls itself, as the &lt;code&gt;openclaw&lt;/code&gt; label, and returned this, lightly&lt;br&gt;
condensed for readability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Daily Ops Briefing, 2026-06-29

### 1) Server: manage.cylonix.io (cylonix-manage / manage-master-1)
- Health: Up. check_host responding as root on Linux 5.4.
- Uptime: 449 days, ~21h (no recent reboots).
- Load: 2.22 / 1.68 / 1.40 (1/5/15 min), trending up but moderate.
- Memory: 3.9 GB total, ~2.5 GB used, ~1.0 GB available. Tight but OK.
- Disk /: 84% used (67.6 GB of 81 GB; ~13 GB free). Worth watching.
- Cylonix services running (from pod logs):
  - Controller plane: cylonix, cylonix-ui, supervisor, log-collector
  - Data stores: postgres, redis, influxdb, etcd, prometheus, keycloak, ipdrawer
  - Infra: k8s control plane, Cilium + operator, CoreDNS, kube-proxy, Traefik

### 2) Email: 20 unread in Inbox
- Cylonix builds (action): TestFlight iOS 1.0.12 (13) and macOS 1.0.12 (88) ready to test.
- Account/billing (action): Azure "update payment method"; Capital One offer (skippable).
- Search Console: "Improve presence" for openscopeai.com and kidfence.ai.
- Legal/policy (FYI): Google Workspace ToS update.
- Noise: DigitalOcean, NVIDIA, Figma, LinkedIn, Redfin.

Action items:
1. Test new iOS (13) and macOS (88) Cylonix builds.
2. Review billing-program changes.
3. Verify Azure payment method is current.
4. Keep an eye on cylonix-manage disk (84%).

All four OpenScope calls returned cleanly, no policy denials.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A genuinely useful briefing: it caught the disk creeping toward full, surfaced&lt;br&gt;
the builds waiting to be tested, and flagged the billing reminder. And every&lt;br&gt;
privileged step underneath it was a scoped, audited OpenScope call.&lt;/p&gt;

&lt;h2&gt;
  
  
  The harness, in plain terms
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2zuok2gb2v299uqxbu4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2zuok2gb2v299uqxbu4.png" alt="Filter raw privilege vs expose only scoped access" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step back and look at what OpenClaw was actually holding while it did this work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a low-permission label, &lt;code&gt;openclaw&lt;/code&gt;, not a root shell and not a Mail key&lt;/li&gt;
&lt;li&gt;exactly six actions, four read-only server verbs and two read-only mail verbs&lt;/li&gt;
&lt;li&gt;two jobs that are separate principals: the server grants and the mail grants do not overlap&lt;/li&gt;
&lt;li&gt;a per-call policy check, with &lt;code&gt;deny&lt;/code&gt; overriding &lt;code&gt;allow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;an audit record of every decision, allow and deny, in &lt;code&gt;~/.openscope/audit.jsonl&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And what it was not holding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no &lt;code&gt;ssh&lt;/code&gt; to the host, the broker holds the root-owned key&lt;/li&gt;
&lt;li&gt;no &lt;code&gt;osascript&lt;/code&gt;, the broker holds the automation approval&lt;/li&gt;
&lt;li&gt;no &lt;code&gt;restart_service&lt;/code&gt;, no &lt;code&gt;write_file&lt;/code&gt;, no path outside &lt;code&gt;/var/log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;no ability to edit the rules that confine it, those are root-owned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a prompt ever talks OpenClaw into trying something outside that list, the&lt;br&gt;
result is an &lt;code&gt;(exit 3)&lt;/code&gt; and a line in the audit log, not an incident. The&lt;br&gt;
interesting question stops being "what could the agent reach if it went wrong,"&lt;br&gt;
and becomes "are these six actions the right six." That is a much better&lt;br&gt;
question to own.&lt;/p&gt;

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

&lt;p&gt;OpenScope is open source. The broker, the MCP server, and these app definitions&lt;br&gt;
all live in the public repo.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/cylonix/openscope" rel="noopener noreferrer"&gt;https://github.com/cylonix/openscope&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Releases: &lt;a href="https://github.com/cylonix/openscope/releases" rel="noopener noreferrer"&gt;https://github.com/cylonix/openscope/releases&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So go ahead: give OpenClaw a real job on the production server. Just hand it the&lt;br&gt;
actions, not the keys.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I measured what my AI coding agent did to production. It SSH'd in as root 1,508 times.</title>
      <dc:creator>Randy Cylonix</dc:creator>
      <pubDate>Sun, 14 Jun 2026 23:13:03 +0000</pubDate>
      <link>https://dev.to/dryricenoodle/i-measured-what-my-ai-coding-agent-did-to-production-it-sshd-in-as-root-1508-times-bd3</link>
      <guid>https://dev.to/dryricenoodle/i-measured-what-my-ai-coding-agent-did-to-production-it-sshd-in-as-root-1508-times-bd3</guid>
      <description>&lt;p&gt;I have been running Claude Code on auto mode for about a month. It is genuinely great. It also made me uneasy in a way I could not name, so I did the boring thing and looked at what it had actually done.&lt;/p&gt;

&lt;p&gt;I grepped roughly 30 days of my own Claude Code session transcripts (the JSONL files the CLI keeps) for privileged operations: anything that shelled out to sudo, ssh, scp, or rsync against a non-lab host. This is not rigorous telemetry. It is a regex sweep over my own history, so treat the numbers as the right order of magnitude, not an audited figure. But the shape was clear enough to change how I work.&lt;/p&gt;

&lt;p&gt;In that window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;174 local sudo escalations. Roughly half were just reading root-owned files.&lt;/li&gt;
&lt;li&gt;1,508 raw root SSH/scp/rsync sessions into production hosts, 1,288 of them to a single box.&lt;/li&gt;
&lt;li&gt;Of those 1,508, about 6 in 10 were read-only inspection: docker ps, tailing logs, reading a config. They never needed root at all.&lt;/li&gt;
&lt;li&gt;A tail I did not enjoy finding: docker cp hot-patches into running prod containers, a couple of UPDATE/DELETE statements on a production database, live nginx edits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The part that bothered me most is not the number. It is that the number coexists with this: the same agent, on full auto, still stops and asks me for sudo to install a test build on my Mac. It refuses to run a local package install, but it SSHes into production as root on its own. That is backwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it is backwards
&lt;/h2&gt;

&lt;p&gt;The agent decides "is this safe?" with a classifier, and a classifier is a probabilistic judgment that mis-fires in both directions. It over-blocks safe work: installing a signed package, rebuilding a test binary, the mundane local things that happen to need sudo. And it under-bounds genuinely dangerous work: root on a production host gets a probabilistic "this is probably fine," not a hard wall. I cannot tune that classifier, I cannot audit it, and I cannot prove what it will decide tomorrow when the prompt is slightly different. One fuzzy dial controls everything from apt install to a write on prod.&lt;/p&gt;

&lt;p&gt;The two failure modes feed two different pains. The over-blocking means I babysit the safe stuff, which is the thing auto mode was supposed to free me from. The under-bounding means I do not trust it near anything with real users, so I do production by hand, which is the thing an agent was supposed to help with. Auto mode handed me the worst of both.&lt;/p&gt;

&lt;p&gt;The deeper issue is where the decision lives. The "should I do this?" check runs inside the agent, against config the agent can read and, in principle, change. Turning the dial down to stop the nagging also turns it down on the dangerous side. You cannot say "auto-approve the safe class, hard-bound the dangerous class," because there is only one dial and it is a guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did about it
&lt;/h2&gt;

&lt;p&gt;So I moved the decision out of the agent. The result is a small open-source tool, OpenScope. It is an action broker: a separate process between the agent and the system. The agent never gets sudo or an SSH key. It gets named, scoped actions like restart_service, install_pkg, tail_logs, read_note. The real credential stays inside a root-owned daemon, and the policy that says which actions the agent may call lives in root-owned files the agent cannot edit. Even a fully compromised agent can only invoke the verbs it was granted, with the parameters it was scoped to.&lt;/p&gt;

&lt;p&gt;New capabilities are not ambient. When the agent needs a new power it writes a proposal, a plan step reviews it (read-only, no sudo), and a human applies. The plan is the interesting part, because it makes the consequences legible before anything happens. Here is the real output for granting the agent the ability to install a signed package:&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%2Fbg2fg13we7gfxdq9p641.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%2Fbg2fg13we7gfxdq9p641.png" alt="The plan to grant install_pkg: one high finding that requires every package be signed by my Developer ID AND located under a pinned path." width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The high finding is not a failure, it is the point. Installing a pkg runs scripts as root, so the grant is gated to packages that are both signed by my Developer ID and under one specific path. After this, Claude Code installs my new builds on its own. The sudo nag is gone, and it did not become a blanket "agent can sudo."&lt;/p&gt;

&lt;p&gt;Here is the one that lets the agent deploy this project's own marketing site. The command is pinned at apply time, so the agent can trigger it but cannot change a character of it:&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%2Fu43eej5mdkvcf3u9lrar.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%2Fu43eej5mdkvcf3u9lrar.png" alt="The plan to grant the web deploy: a pinned root command, and SSH-NO-BYPASS verified live that no key reaches the host." width="800" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It logs in as root, which the plan flags loudly. But the command is fixed, it touches only the marketing container, and the broker verifies live that no SSH key on my machine can reach that host outside the broker. The line I care about is "no ~/.ssh key reaches the target; verified live." That is the difference between a classifier saying "probably fine" and a system proving the agent has no side path to the key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not only infrastructure
&lt;/h2&gt;

&lt;p&gt;The same broker gives my personal-assistant agent scoped access to Apple Notes and Mail instead of my whole mailbox: it can list or read a specific note, or draft a mail, each one gated and logged, while the signed app holds the macOS Automation grant and the agent never does. a16z described this exact gap on their AI + a16z podcast recently: &lt;strong&gt;"There's a huge opportunity for startups to create these proxies ... if someone would give me a scoped Gmail, I'd adopt it today."&lt;/strong&gt; That is the whole idea. An agent should get a scoped proxy to whatever it touches, a production host, your package manager, or your inbox, and the credential plus the full surface should stay behind the proxy.&lt;/p&gt;

&lt;p&gt;The obvious objection at team scale is verb explosion, a custom action per person. It is not. The verbs are a shared catalog, authored once and shipped to every broker, and most are generic and parameterized: one restart_service or tail_logs covers everyone. What varies per agent is policy, which verbs it may call and with what scope, which is just data, the same model as IAM grants. A root-owned bounds file enforces invariants across the whole fleet (no agent may hold a readable key, a cap on how many hosts one agent can reach) regardless of any individual grant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The point
&lt;/h2&gt;

&lt;p&gt;I do not think auto mode is the problem. I think a single probabilistic dial controlling both "install a package" and "drop a production table" is the problem. The fix is not a better classifier. It is to stop asking the agent to police itself, and instead give it a small set of scoped powers bounded by something it cannot rewrite. Then the safe stuff actually runs unattended, and the dangerous stuff is bounded by a rule you can read.&lt;/p&gt;

&lt;p&gt;OpenScope is open source and runs fully local, no server: &lt;a href="https://github.com/cylonix/openscope" rel="noopener noreferrer"&gt;github.com/cylonix/openscope&lt;/a&gt;. The honest caveat from the top still stands, so if you run coding agents, grep your own transcripts and tell me whether 1,508 in a month is high or low. And if you read the design, I most want to hear where you think the broker boundary leaks.&lt;/p&gt;

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