AI agents have a boundary problem that becomes more visible as soon as they can touch a real machine.
Keep the model inside a chat box and the boundary is simple, but the workflow is limited.
Give the model access to files, a terminal, a browser and desktop automation and the workflow becomes much more useful — but a raw shell or broad credential can turn the entire operating-system account into one permission.
That creates a practical trade-off:
capability or control.
I wanted to see whether machine access could stay useful without making account-level authority the default interface.
That became RunOnMine, an open-source, local-first MCP security gateway for controlled AI access to files, terminals, browsers and desktop apps.
Repository: https://github.com/ademisler/RunOnMine
The first public beta is available for macOS, Windows and Linux.
This article is about the architecture behind the boundary, the failure modes I wanted to avoid, and why I do not describe RunOnMine as a sandbox.
The product boundary
RunOnMine is not a collection of MCP tools with a permission dialog added later.
The permission boundary is the product.
A request should not become executable just because an authenticated client knows the name of a tool.
The system needs to answer several questions together:
- Which connector delivered the request?
- Which requester is behind that connector?
- Which tool is being called?
- Which resource is being targeted?
- What does the machine owner's local policy say?
- Does this exact action require approval?
The simplified flow is:
AI request
|
v
requester + connector identity
|
v
local resource policy
|
+---- allow ----> execute
|
+---- ask ------> local approval ----> execute / deny
|
+---- deny --------------------------> stop
The important detail is where the decision happens.
It happens on the machine being controlled.
Remote authentication can narrow authority, but it does not get to expand local policy.
A tool name is not an authorization decision
Consider a filesystem write.
fs_write tells us what class of operation is requested. It does not tell us whether the request should be allowed.
The authorization decision also depends on the path, the requester, the connector and the exact action.
The same is true for shell execution.
git status inside one selected repository and a destructive command from a remote connector should not collapse into the same boolean permission just because both use shell_exec.
This is why RunOnMine separates tool capability from local authorization.
Selected roots instead of account-wide filesystem access
A coding agent usually does not need my entire home directory.
It may need one repository.
RunOnMine asks the owner to select filesystem roots and keeps file operations inside those roots. The implementation uses capability-oriented, descriptor-relative operations and rejects paths that escape the configured boundary.
This changes the failure surface.
A prompt mistake or prompt injection inside one project should not automatically turn into visibility over unrelated repositories, SSH material, browser state and personal documents simply because the process happens to run as the same OS user.
The rule I wanted was simple:
grant the project, not the account.
Approval should be about the action, not the button
Human-in-the-loop security becomes weak if approval means "trust this tool forever".
An owner needs to understand what is about to happen.
RunOnMine binds approval to the requester and the concrete action context. If the meaningful action changes, the previous approval should not silently become permission for the new one.
Approval is also local-only.
There is no MCP tool called approve_my_request that a remote agent can invoke after asking for something dangerous.
That sounds obvious when written down, but keeping the approval authority outside the remote tool surface is an important part of the design.
Shell execution is not a sandbox
This is the limitation I care most about stating clearly.
If a shell command is permitted, it runs with the authority of the RunOnMine operating-system account.
RunOnMine can decide whether a request reaches execution. It can scope the working directory, bind the decision to the requester, require approval, bound retained output and terminate a process tree on timeout.
It cannot turn an arbitrary permitted command into harmless code.
That is why the project calls itself a security gateway and approval system rather than a sandbox.
The optional privileged helper is a separate installation boundary. It is not installed by normal setup, and remote connectors do not receive administrator execution through the standard policy profiles.
Browser control adds a network boundary
A separate browser profile is useful, but it is not the whole browser security problem.
If a remotely driven browser can freely reach localhost or private networks, the browser itself can become a bridge to services the external requester could not otherwise reach.
RunOnMine's protected browser path therefore treats destination access as part of the policy boundary.
The browser uses an isolated profile and protected network behavior. The implementation checks destinations and uses a controlled proxy path for protected automation rather than assuming profile isolation solves private-network access.
This is also why attaching to an arbitrary existing browser through CDP is a different trust decision: launch-time protections cannot be retroactively applied to a process that was started outside that boundary.
Remote access without a raw public MCP listener
I did not want "use this remotely" to mean "bind the MCP server to 0.0.0.0".
RunOnMine keeps its HTTP MCP listener on loopback.
Remote connectivity is carried outward through supported connector/tunnel models. The current beta supports local stdio, opt-in authenticated loopback HTTP, Cloudflare connector modes and OpenAI Secure MCP Tunnel integration.
The network transport still does not become the policy authority.
Remote callers remain subject to local policy. Dangerous remote actions cannot approve themselves, and remote administrator execution is denied by the safety ceiling.
Emergency Lock is different from uninstall
A system that can perform real machine actions needs a cheap revocation path.
The owner should not have to delete configuration or dismantle the installation to say "stop now".
RunOnMine has an Emergency Lock operation for that purpose.
It stops the active agent/connectors, rejects pending approvals, removes temporary grants, revokes relevant OAuth state and invalidates temporary credentials used by supported connector paths.
The local configuration remains, so the owner can inspect and recover deliberately later.
That distinction matters:
revocation is a runtime operation, not an uninstall workflow.
Audit should not become a second secret store
Machine automation needs an audit trail.
But logging everything naively can create a new security problem.
Raw command payloads, credentials, URLs and machine paths can contain sensitive data. A debug bundle that simply archives an application state directory is easy to create and difficult to trust.
RunOnMine keeps a tamper-evident audit chain while avoiding raw secret storage in audit records. Support material is built from bounded, redacted diagnostics instead of blindly copying internal state.
The audit trail is not meant to make a compromised user account tamper-proof. It is meant to make normal machine actions and unsophisticated modification visible without turning observability into another credential leak.
Release engineering is part of the boundary
Security claims are difficult to reason about when the tested source and the downloadable artifact are ambiguous.
For the public beta I wanted release evidence tied to an exact candidate.
The repository records the frozen source candidate and release gates in machine-readable files. Platform acceptance evidence names the source revision and artifact hashes. The release also includes SHA-256 checksums and target-specific CycloneDX SBOMs.
That does not replace code signing or an external review.
It makes the beta evidence inspectable.
What the beta does not claim
RunOnMine is prerelease software.
The current beta has intentionally visible limitations:
- macOS is ad-hoc signed, not Developer ID signed/notarized
- the Windows installer is not Authenticode signed
- an independent external security review is still pending
Those gaps are documented because a security-oriented tool should not hide its own distribution trust limitations.
Public beta
RunOnMine v0.1.0-beta.1 is available for macOS, Windows and Linux.
It is written in Rust and licensed under Apache-2.0.
GitHub: https://github.com/ademisler/RunOnMine
Website: https://runonmine.github.io/
The main lesson from building it is that giving an agent more tools is the easy part.
The harder question is deciding where automation ends and owner authority begins.
For people building or using MCP agents: which machine actions would you allow automatically, and which would you never allow without an exact local approval?
Top comments (0)