Agent permissions are moving from interface design to enforcement design
The hard question is no longer whether an agent asks before running a dangerous command. It is whether every path to a consequential effect passes through a component the model cannot bypass, reinterpret, or persuade.
On August 28, the MIT-licensed Talos project appeared on Hacker News with the description “an AI agent with a permission kernel between model and shell.” Its repository had eight stars and the discussion had 14 points and seven comments when checked on August 29. Those numbers do not establish adoption. They do make the implementation timely: the repository exposes its policy decisions, capability tokens, target extraction, approval flow, event log, red-team cases, and limitations for inspection.
The broader signal is stronger than one young repository. A July paper surveyed 21 proposals for user permissions in AI agents. Other July work explicitly separates what an agent can technically do from what it is allowed to do. Landlock is now a practical unprivileged Linux isolation mechanism, and the Apache-2.0 nono project applies it to agent and delegated-tool sandboxes. OpenAI's current Agents SDK documentation separately warns that sandbox files and runtime users are not the same thing as model permissions, approval policy, or API credentials.
That distinction matters because an approval prompt can be perfectly designed while the executor remains reachable elsewhere. A model may call a lower-level shell wrapper, use an allowed interpreter to reach a denied file, cause a package script to spawn a child process, reuse a credential through a different API path, or retry an approved request with changed arguments. The interface said “ask.” The system did not guarantee “only after this exact ask.”
A prompt can record consent. Only the execution path can enforce authority.
A permission kernel is an architectural role, not a marketing label
Here, kernel means the smallest component that all effects must cross. It may be an in-process policy object, a broker, a sidecar, a gateway, or a separate service. It is not automatically an operating-system kernel and should not borrow OS-level credibility unless it actually uses OS enforcement.
A useful permission kernel has four properties. First, raw runners are unreachable without a capability. Second, the kernel derives the real resource targets from typed tool arguments rather than trusting a model-supplied “target” field. Third, authority is narrower than the request: one identity, action, resource set, argument digest, consequence class, time window, and use. Fourth, the executor validates authority again immediately before the effect and emits a durable result.
| Control | Weak version | Kernel version |
| Tool selection | Prompt says which tools are allowed | Registry exposes only callable adapters |
| Target | Model declares a path or URL | Adapter extracts and canonicalizes actual targets |
| Approval | User approves a prose summary | Approval binds exact arguments and consequence |
| Lifetime | Permission lasts for the session | Short TTL, single use, explicit audience |
| Execution | Runner trusts the caller | Runner refuses without a valid capability |
| Evidence | Chat transcript says what happened | Intent, decision, token, result, and refusal are logged |
Talos uses three verdicts: ALLOW, NEEDS_HUMAN, and DENY. The ordering matters. A hard denial returns before approval, so a human “yes” cannot override a catastrophic rule. During unattended runs, NEEDS_HUMAN becomes DENY; an old question is not parked until somebody reflexively clicks it hours later.
The only safe executor is one that cannot run naked
1. ProposalModel emits a typed tool call with arguments and task context.
2. Target extractionTrusted adapter resolves canonical paths, hosts, records, identities, and side effects.
3. Policy decisionKernel combines identity, task, targets, data class, reversibility, budget, and mode.
4. Capability mintExact arguments, audience, nonce, expiry, and one-use counter are signed or kept in protected state.
5. Consequence recheckExecutor verifies token and mutable facts immediately before running.
6. ReceiptIntent, decision, effect, output digest, rollback state, and refusal reason are durable.
Do not let every tool implement this independently. Duplicate policy code drifts, and one forgotten adapter becomes a bypass. The tool registry should require a target extractor, consequence classifier, executor audience, and receipt serializer. If an adapter cannot state what it touches, the default is not “best effort.” It is unavailable.
Also avoid letting the model create a second plan after reading tool output. A result may contain prompt injection. If an announced three-step run can expand to forty steps because a web page said so, the step budget is advisory. Freeze the plan identity and budget outside the model, then treat any proposed expansion as a new authorization request.
Bind authority to the exact action, not the category
“May use GitHub” is too broad. “May add label needs-review to issue 184 in repository acme/payments before 14:05 UTC, once” is enforceable. The narrower statement can be compared with the actual request and rejected after a changed argument, expired approval, or replay.
version: 1
tool: github.add_label
principal: agent:triage-bot
delegated_by: user:42
audience: executor:github-v3
resource:
repo: acme/payments
issue: 184
arguments:
label: needs-review
limits:
uses: 1
expires_at: 2026-08-29T14:05:00Z
context:
task_id: triage-2026-08-29-17
policy_version: permissions-7
consequence: reversible-metadata-write
Canonicalization belongs before the decision. Resolve symlinks, normalize repository identities, parse URLs, expand indirect selectors, and enumerate wildcard populations when practical. A rule allowing writes under /workspace is false comfort if /workspace/out is a symlink to ~/.ssh. A network rule allowing api.example.com is incomplete if redirects or DNS rebinding can move the connection.
Recheck mutable facts. A file can change between approval and execution; Talos documents hash verification before acting. A database row can change state, a user can lose permission, a deployment can move, or a price can change. Bind stable facts into the token and query volatile facts at the consequence boundary.
One kernel does not replace four enforcement layers
| Layer | What it controls | What it cannot prove |
| Application policy | Task intent, tool, arguments, budget, consequence | That the process cannot reach another resource |
| OS confinement | Filesystem, process, network, syscall, child inheritance | Business authority inside an allowed API |
| Target-system authorization | Repository, record, tenant, role, amount, method | That the request matches the user's current intent |
| Human decision | Ambiguous or consequential exception | That execution will match the approved bytes |
Landlock demonstrates why OS enforcement is valuable: once a process restricts itself, it cannot widen those rights later. nono layers policy, canonical path grants, network control, credential injection, and separate tool sandboxes on top. But an OS sandbox still cannot decide whether an agent may merge a pull request. If network access to GitHub is allowed and the token can merge, the target system must enforce repository and method scope.
The reverse is also true. A perfectly scoped GitHub token does not stop a local shell from reading unrelated credentials or modifying another checkout. Application policy and remote IAM need OS confinement underneath them. This is defense in depth with different failure domains, not four copies of the same allowlist.
A minimal authorization path has no policy-free shortcut
def invoke(call, principal, task):
adapter = registry.require(call.tool)
targets = adapter.extract_targets(call.arguments)
facts = canonicalize_and_classify(targets)
decision = kernel.decide(
principal=principal,
task=task,
tool=call.tool,
arguments=call.arguments,
targets=facts,
)
ledger.write_intent(call, facts, decision)
if decision.verdict == "DENY":
return refused(decision.reason)
if decision.verdict == "NEEDS_HUMAN":
return approval_request(decision.exact_request)
capability = kernel.mint(decision, uses=1, ttl_seconds=30)
return adapter.execute(call.arguments, capability)
def execute(arguments, capability):
verified = kernel.consume(capability, audience="shell-v4")
recheck_hashes_and_identity(verified)
result = confined_runner.run(arguments)
ledger.write_result(verified, result)
return result
The executor receives the capability, not a Boolean. A Boolean can be cached, confused across requests, or applied to changed arguments. A capability carries or references the conditions that made the decision valid. Consumption is atomic so concurrent retries cannot both succeed.
{
"event": "exec.result",
"task_id": "triage-2026-08-29-17",
"capability_id": "cap_7f31",
"tool": "github.add_label",
"argument_digest": "sha256:82b9...",
"policy_version": "permissions-7",
"verdict": "ALLOW",
"effect": {"repo": "acme/payments", "issue": 184, "label": "needs-review"},
"result_digest": "sha256:1d6a...",
"rollback": "remove-label",
"completed_at": "2026-08-29T14:04:12Z"
}
Test the bypasses, not only the policy examples
A permissions suite should prove that an effect cannot occur through alternate syntax, a child process, a redirect, a symlink, a stale approval, or a second adapter. Talos points readers to its adversarial suite; the repository added a public “179 ways to break an agent” page on August 28. Treat the number as project-reported. The useful practice is keeping attacks executable on every change.
- Call every raw runner without a capability and require a hard failure.
- Change one argument after approval and require a digest mismatch.
- Replay the same capability concurrently and allow at most one effect.
- Swap a path for a symlink, hard link, alternate casing, traversal, or mount alias.
- Test redirects, DNS changes, alternate ports, and an allowed client invoking a forbidden method.
- Spawn shells, interpreters, package scripts, build hooks, and subprocess trees.
- Corrupt policy, event-log, and identity state; verify failure tightens authority.
- Put instructions in tool output and confirm they cannot expand plan or permission.
- Expire approval between decision and effect; require a new request.
- Verify refusals, partial effects, rollbacks, and interrupted runs are visible in receipts.
Run negative tests against the deployed binary and real adapters, not only policy-unit functions. A unit test can prove decide() returns DENY while another execution path never calls decide().
Failure modes that permission prompts hide
| Failure | Why it passes review | Control |
| Second executor | Main shell is gated; helper is not | Registry-enforced capability interface |
| Model-supplied target | Summary names a safe file | Trusted extraction from actual arguments |
| Approval replay | Original action was legitimate | Nonce, one use, short TTL, atomic consume |
| TOCTOU swap | File was safe at approval time | Hash and identity recheck before effect |
| Child-process escape | Parent command looks harmless | Inherited OS confinement and process policy |
| Overbroad remote token | Network destination is allowed | Target-system method/resource authorization |
| Human rubber stamp | Frequent prompts train reflexes | Hard denials, batching, budgets, unattended ceiling |
| Receipt without consequence | Log says command exited zero | Record observed resource change and verification |
Talos states that it is not a multi-tenant boundary and that anyone able to run code inside its process can reach the token mint. That limitation is important. An in-process kernel can still remove accidental and model-mediated bypasses, but hostile co-tenants require process, VM, container, language-runtime, or service isolation appropriate to the threat.
Adopt a permission kernel by shrinking the effect surface
- Inventory every path from a model tool call to a real effect, including helpers and delegated tools.
- Classify effects by data, reversibility, external audience, money, identity, persistence, and blast radius.
- Require target extractors and consequence classifiers before registering a tool.
- Put raw executors behind an interface that accepts capabilities, not trust flags.
- Add OS confinement for the agent and each high-risk delegated tool.
- Scope remote credentials by agent, task, tenant, repository, method, and duration where supported.
- Start in observe-only mode, but measure would-deny calls and false permissions against real tasks.
- Turn on hard denials for secrets, persistence, system paths, and known catastrophic effects first.
- Add narrow human approval only where a qualified person can inspect exact consequences.
- Gate production on adversarial bypass tests, receipt completeness, rollback drills, and removal of every legacy runner.
Do not begin with an “autonomy level” that grants new rights. A useful dial can only tighten a fixed ceiling. If level five means “anything goes,” the dial is another authority source. Define the maximum through policy and infrastructure, then let runtime modes reduce what can happen.
Frequently asked questions
What is an AI agent permission kernel?
It is the non-model choke point that evaluates a proposed action, derives its real targets, returns an enforceable verdict, and makes the executor reject any call without valid narrow authority.
Is it the same as an OS kernel?
No. An application component may play the kernel role without kernel privilege. Use precise language and add real OS confinement where the process, filesystem, network, and child-process threat model requires it.
Can capability tokens replace human approval?
They solve a different problem. Human approval supplies a decision for an ambiguous or consequential request. The capability binds that decision to exact machine-checkable conditions so execution cannot silently drift.
Should permissions use allowlists or denylists?
For high-consequence effects, make callable tools, targets, methods, and credentials allowlisted. Denylists remain useful as an additional catastrophic floor, but they cannot enumerate every alternate path to an effect.
Is Talos production-ready?
The project labels current releases alpha, had eight stars at the evidence check, and explicitly limits itself to one operator and one machine. Study and test its design; do not treat the launch as independent assurance or broad production proof.
Sources and further reading
Public sources were checked on August 29, 2026. Repository counts and implementation details can change.
Related guides
Bind browser consent, OAuth state, PKCE, grants, and token use to the same authenticated user and approved scope set.
Add consent, sender identity, audience, budget, deduplication, reputation, and feedback to the general authority boundary.
Design fewer, higher-information checkpoints without mistaking prompts for boundaries.
Map the host, network, credential, and external-system boundaries that remain.
Turn a trace into evidence of the exact authorized consequence.