cd /news/ai-agents/talos-ai-agent-super-secure Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-97457] src=github.com β†— pub= topic=ai-agents verified=true sentiment=↑ positive

Talos AI Agent Super Secure

Talos, an open-source autonomous agent from talos-agent.ch, runs on a user's own machine and executes tools only after a deterministic security kernel approves each action via a capability token valid once for 30 seconds, with 164 adversarial scenarios tested on every change. The agent refuses to run without a sandbox on Linux or macOS, and ships with zero identities that may command it, positioning itself as a secure alternative to conventional AI agents that request shell access.

read27 min views1 publishedAug 14, 2026
Talos AI Agent Super Secure
Image: Michielbdejong (auto-discovered)

An autonomous agent you can hand a shell to,because it can prove what it will not do.

talos-agent.ch Β·

Field manualΒ·

Changelog

Talos runs on your own machine, takes instructions over a chat channel, thinks through a language model, and executes tools β€” but only after a deterministic security kernel has ruled on the action. The model proposes. It never decides.

   message ──▢ event log ──▢ reason ──▢ ╔══════════╗ ──▢ capability ──▢ execute
                                        β•‘  KERNEL  β•‘        token          β”‚
                                        β•šβ•β•β•β•β•β•β•β•β•β•β•                       β–Ό
                                             β–²                     verify ──▢ receipt
                              the only place effects are authorised

Authority | a token bound to exact arguments β€” valid once, for 30 seconds | A tool without a target extractor | DENY by construction, not by a rule someone wrote | Ships with | zero identities that may command it | The shell | sandboxed, or it refuses to run at all |

curl -fsSL https://talos-agent.ch/install.sh | less   # read it first
curl -fsSL https://talos-agent.ch/install.sh | sh     # then run it

The installer verifies the signature and the checksum, runs the full suite β€” and then stops. Nothing starts listening until you say so.

Contents β€” twenty sections, in the order they matter

Start here Why this exists Β· What it does not do Β· Install

Using it A session in the terminal Β· Work on the side Β· What it remembers Β· Seeing what it did Β· Commands

How it holds How the kernel decides Β· The autonomy dial Β· Timed runs Β· Announced plans Β· The browser that only reads Β· Delegating Β· Identity

Evidence What a run looks like Β· Audit trail Β· Tools Β· Architecture Β· Roadmap

Every capable agent eventually asks for shell access. At that moment you are trusting a language model with your machine, and the usual answers are unsatisfying: either it asks you to confirm everything (and you stop reading the prompts by day three), or it runs free behind a regex blocklist and you hope.

Talos takes a third position. Authority is a token, not a list. Every effect is authorised individually, bound to its exact arguments and targets, valid once, for thirty seconds. Forgetting to call the gate does not produce an unchecked effect β€” it produces no effect at all, because the raw runners are unreachable without a token.

That design is testable, and it is tested: 164 adversarial scenarios run on every change and try to get an effect past the kernel. They are in redteam.py. Read them before you trust anything written above.

Stated plainly, because a security claim without its limits is marketing:

It runs under bubblewrap on Linux andrun_shell

needs a sandbox the platform can actually provide.sandbox-exec

on macOS. Where neither is available itrefusesrather than running unprotected, so on such a host the shell tool is simply unavailable until the operator overrides it on purpose (TALOS_SANDBOX_ALLOW_UNCONFINED=1

).It is not a multi-tenant security boundary. One operator, one machine. Anyone who can run code in the process can reach the token mint.There is no gateway, no setup portal and no Each was considered and left out for the same reason. A gateway terminates foreign identities and thenconfig.yaml

.claimschannel:id

to the kernel instead of proving it, so identity checking would hang on a component outside the kernel. A web portal is a network service with its own authentication β€” by definition a second source of permission beside the allowlist. And a second config file would mean two truths about who may command it, when the path floor protects exactly one. What weakens a guardian is not the number of commands; it is a second way in.It does not defend against a malicious model. It defends against amistakenone, and against prompt injection arriving through tool output. Those are different threats.Search needs no account. WithoutTALOS_BRAVE_API_KEY

it used to refuse; now it answers over DuckDuckGo (theddgs

package, imported at call time). A key decideswhichprovider answers, notwhetherone does.It has two ways in, and both of them fetch(Telegram long-polling, mail over IMAP). That is the rule, not an accident: an inbound webhook would need a port the world can reach, which turns "outbound only" into "publicly reachable". Mail sits atTrust.ASK

β€” an address proves no account, so it may ask and receive answers but never approve anything. WhatsApp is delivery-only for the same reason.It hears locally or not at all.hear

transcribes a recording with faster-whisper on the machine it runs on β€” what was said is often the most private thing in a day, and sending it to somebody else's model to be understood is the one place where "runs on your machine" would be traded for convenience. It is an ordinaryREAD

with the file as its target, so a recording under~/.secrets/

is refused without the module knowing anything about secrets. The model loads on first use, not at boot.It can see, on a real file. A photo you send is fetched intoworkspace/inbox/

and the note carries the path, sosee_image

has something to point at β€” and the kernel has a target to judge. The fetch asks the allowlist first: the channel parses updates before the kernel has ruled on identity, so an unconditional fetch would let anyone who finds the bot write files to your disk.speak

writes a WAV offline (piper).It can take one still out of a video.grab_frame

runs ffmpeg once and puts a single picture in the inbox;see_image

looks at it afterwards. Both the video and the picture are targets the kernel judges, so a video under~/.secrets/

is refused exactly like a recording β€” without that, calling a file a video would have been the way around the secret floor. Where the picture lands is derived by the kernel, not chosen by the model, and it is deliberately one frame rather than a series: fanning ffmpeg out over a whole film and encoding every frame is how a small machine ends up unreachable.It does not generate pictures or video, on purpose. Reading one is aREAD

with a target; making one is a paid call to somebody else's model, and it was removed rather than kept around waiting for credit. Seeing stays.

Requires Python 3.11+ and a working Claude Code CLI (the reasoner runs it headless via OAuth β€” no API key, no per-token billing).

git clone https://github.com/talos-kernel/talos.git
cd talos
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt

python -m talos setup                    # asks three things, writes a file, stops
python -m talos doctor                   # what is still missing
python -m pytest tests/ -q               # 1596 tests
python redteam.py                        # 164 adversarial cases
python -m talos                          # run it

setup

proves what it can rather than trusting what you type: the token goes to getMe

, and your identity comes from a real message you send your own bot β€” not from a number you copy. A wrong token announces itself on the first poll; a wrong identity does not, and a stranger's identity never does. It writes the file and stops; starting is yours.

Later, a single part can be redone without repeating the rest:

python -m talos setup model              # switch what it thinks with
python -m talos setup mail               # add the second way in (IMAP)
python -m talos config list              # every key, its kind, whether it is set
python -m talos config set TALOS_MODEL claude-fable-5
python -m talos models --refresh         # ask each provider what it offers now
python -m talos status                   # what it did last
python -m talos chat                     # a session here; approvals at a terminal
python -m talos ask "how many …?"        # one turn from here, answer on stdout
python -m talos events --tool run_shell  # what happened β€” read-only, filterable
python -m talos why 4831                 # why that was allowed or refused
python -m talos review                   # what this installation should change
python -m talos report --out audit.txt   # what was done and what was refused

A full walkthrough β€” install, identity, the session, every command, and the ones that are missing on purpose β€” is at ** talos-agent.ch/docs**.

ask

is not a second way in β€” it is a channel like any other, with the same protocol and no special right. Two consequences follow, and both are the point of it. Whoever types there must be in the allowlist as cli:<uid>

, exactly like a Telegram number; a shell next to the agent is not an argument for granting it anything. And the turn always runs under the unattended ceiling: a one-liner waits for nothing, so NEEDS_HUMAN

becomes DENY

and says so. Approve in the chat, where somebody is actually looking.

It also refuses to run inside the agent's own sandbox. Otherwise the agent could start talos ask

from its shell and give itself orders β€” no channel, no foreign identity, nobody reading along.

ask

answers once and leaves. For anything that takes more than one turn there is talos chat

β€” the same channel name, the same identity, the same conductor, the same kernel. The loop is line for line the one the service runs for Telegram; only the messages come from stdin

instead of getUpdates

.

$ talos chat
Talos 0.9.2-alpha  Β·  anthropic/claude-opus-4  Β·  autonomy 3
speaking as cli:1000  Β·  approvals possible β€” you are at a terminal
/help for commands, `exit` to leave

β€Ί what changed in the log today?

The second line is the one to read: it says, before you ask for anything, whether an approval is even possible here β€” so a refusal later is explained in advance rather than guessed at.

The unattended ceiling hangs on the terminal, not on the command. This was the contested decision. Applying ask

's rule to an interactive session would mean chat

could never write anything; dropping it would mean talos chat < jobs.txt

in a cron job looks exactly like a human. So it is measured rather than claimed: both stdin and stdout must be a real tty. A pipe, a redirect, a cron run has none, and there

NEEDS_HUMAN

becomes DENY

as before. Checking only stdin

would let talos chat > out.log

pass as attended β€” nobody reads a question written to a file.The in-session commands are the same ones the messenger has, through the same command centre. A second vocabulary that only worked here is exactly the duplication this project has paid for elsewhere.

Some questions take two minutes, and the conversation should not stand still for them.

β€Ί /background go through /var/log and summarise today's errors

Background #1 started: go through /var/log and summarise today's…
  id bg_a1b2c3d4e5f6 Β· runs unattended, so anything needing approval is refused

A background task is a scheduled run, not a helper carrying your rights. That is the whole security placement, and it saves inventing a new concept. It is not a subagent: that one is born from model text and may therefore only read. Here a human typed the task, so its origin is the same as any other message. But nobody is sitting in front of it β€” so the ceiling from the timed run applies, and anything that would need approval is refused. Putting a question into a chat where a different conversation is running is the most reliable way to land a "yes" on the wrong thing.

Its context is empty: the task and nothing else. Two runs sharing one history write into each other, and afterwards nobody can say which of them said what. The result does not flow back into the history either β€” it arrives as its own message, marked as a report.

If no ceiling is wired, the task is refused rather than run uncapped. A forgotten parameter may only ever allow less.

The conversation is kept per channel, in memory only, bounded by turns and by characters. When the bound is reached the middle is summarised and both ends stay verbatim β€” the head usually carries the actual task, the tail whatever "and that too" refers to.

You:                  set up the backup for the NAS
Agent:                done β€” see /etc/backup.d
Earlier (summarised): discussed retention, settled on 30 days; the NFS
                      mount needed the hard option
You:                  and add the offsite copy

The summary is labelled as one and never disguised as a verbatim turn β€” a summary that looks like something actually said is a claim about words nobody used, and the model could quote it back as if it were. It goes into the prompt as history, never into the standing instructions, and the transcript reaches the summariser explicitly framed as data: otherwise "summarise this" is the most convenient place for an injected line to become an instruction, and a permanent one, because the summary stays.

The bound is not a comfort feature. If summarising fails, the oldest turns are dropped as before. A history that kept growing after a failed summary would turn a cost question into a leak β€” what was said weeks ago would go out again.

The thin feeling with a guardian does not come from missing verbs. It comes from not being able to see what it did and why. Both of these are read-only:

python -m talos events --tool run_shell   # what happened, filterable
python -m talos why 4831                  # why that was allowed or refused

why

names the verdict, the rule that produced it, and the targets it applied to β€” and states that those targets were derived from the real arguments, never taken from what the model wrote. Then it shows the rest of the same run, because "refused, and then?" is the question that makes people stop reading logs.

There is deliberately no talos undo

beside them. /undo

exists, and since talos chat

it is reachable from the command line through the same path the messenger uses. A second one would be convenience against the doctrine.

** config set refuses two whole classes of key**, and that is the point of it. Secrets never go on a command line β€” that lands them in the shell history and in

ps

for every user of the machine. And TALOS_ALLOWED_PRINCIPALS

, TALOS_SECRETS_ENV

, TALOS_MAIL_AUTHSERV_ID

, every TALOS_BASE_URL_<PROVIDER>

and TALOS_WEB_ALLOWED_ADDRESSES

are not settings but policy: whoever writes them does not have to talk the kernel into anything, they reconfigure it. Not settable, not even with a confirmation β€” a confirmation is exactly what gets clicked away.

config get

answers [REDACTED]

for a secret whether it is set or not, so the command cannot be used to find out which doors a machine has.

A key belongs to one provider. There is no single api_key

and no single base url; credentials.py

keeps a route per provider β€” key and address in one piece, resolved at the moment of the call rather than frozen when the reasoner was built, because /model

switches providers while the process keeps running. If the selected provider has no key, the switch is refused and the previous one stays active: no other provider's key stands in for it. Until 2026-08-05 one field held ANTHROPIC_API_KEY or OPENAI_API_KEY

for every provider, so choosing openai-api

sent an Anthropic key to OpenAI as a bearer token β€” a credential disclosure that looks like a typo, because the wrong recipient answers 401

.

There is no default allowlist. TALOS_ALLOWED_PRINCIPALS

must name your identity or the agent refuses to start. A shipped default would be a backdoor printed in the source.

PolicyKernel.decide()

returns exactly three verdicts β€” ALLOW

, NEEDS_HUMAN

, DENY

β€” and the executor calls no runner before it has one.

Class Examples Verdict
Ordinary work ls , git status , tests, builds, writes inside the workspace
runs
Risky but recoverable `curl sh,git reset --hard,rm -rf <path>`
asks you
Persistence & secrets .bashrc , systemd units, ~/.ssh , the agent's own source
asks you
Catastrophic rm -rf / , mkfs , dd to a block device, fork bomb, reboot
refused
Secret reads, system paths cat /etc/passwd , anything under /etc /boot /usr /bin
refused

DENY

returns before the approval check, so a "yes" cannot reach a hardline rule. Approvals are one-shot, five-minute TTL, bound to the exact request, and re-verified against file hashes immediately before execution (TOCTOU).

Targets are derived from real tool arguments, never from a field the model could omit. A tool without a target extractor is DENY

by construction.

/autonomy 0..5

sits above the kernel and can only tighten, never loosen. Level 5 is exactly the unfiltered kernel β€” not "anything goes". A dial that granted rights would be a second source of permission next to the kernel, which is the thing this design removes.

Level Reads Writes Shell
0 refused refused refused
1 asks refused refused
2 free refused refused
3 free asks asks
4 free free in workspace asks
5 free kernel decides kernel decides

The level survives restarts because it lives in the event log. An unreadable log drops to level 0, not to the last convenient value.

/every 90 <task>

repeats on an interval; /every 0 8 * * MON-FRI <task>

takes a cron expression, because an interval can say "every 90 minutes" but never "weekdays at 08:00". An expression is a better clock, not an extra permission β€” what runs afterwards passes the same kernel.

And it passes one ceiling more. During an unattended run NEEDS_HUMAN

becomes DENY

: what may run without asking runs, everything else is reported rather than performed. Not parked until morning either β€” an approval question whose occasion is six hours old is how reflexive clicking starts. So a timed run may do strictly less than something you typed, which is the opposite of how cron usually works.

For a task with several steps the agent may announce the sequence before it starts:

≑ 3 steps β€” collect the log, find the failing service, report

The announcement changes nothing about permission. Every step still passes the kernel one at a time, exactly as if it had been asked for alone, and a step that needs approval still stops and asks. What the announcement does is bind the run:

  • the step budget shrinks from the house limit to what was announced β€” a three-step plan cannot become forty tool calls;
  • the first step that fails ends the run with a report of what ran, what stopped it, and what therefore did not happen β€” instead of the model improvising around the failure, which is how an agent turns a refusal into a bigger second attempt;
  • the plan is read once. A tool result is a stranger's text; if it could install a second, larger plan mid-run, prompt injection would be a way to buy budget.

A step may carry a condition, and the condition is not evaluated by the model:

PLAN: {"goal": "restart and confirm", "steps": [
  {"intent": "restart the service", "check": "contains:rc=0"},
  {"intent": "write the report",     "check": "wrote:/tmp/report.md"}
]}

This closes a real hole. run_shell

returns rc=1

and the executor still records DONE

β€” because the tool ran. Whether the work succeeded lived only in the output text, and the only reader of that text was the model, which has an interest in the answer. A condition is checked in code, in the order announced, against the receipt of the step that ran: ok

(finished cleanly), contains:<text>

(its output held that text), wrote:<path>

(it wrote exactly there, as the kernel derived the target β€” not as the model claimed it).

A condition can only ever withhold a confirmation. If one never comes true, the answer is delivered in full and the system adds its own line beneath it:

βœ• 1/2 announced checks met β€” NOT confirmed done. Still open: step 2 (it writes to /tmp/report.md).

The limit, stated plainly: the model writes the conditions too. A met condition proves that the run's own stated expectation came true β€” not that the job was done well. Someone who sets themselves a trivial condition passes it. What the code guarantees is narrower and still worth a lot: that the condition was really evaluated rather than asserted, that it is shown to you word for word so you can judge its worth, and that the real count also lands in the event log β€” which the model cannot write to, and where a forged verdict line in its prose is contradicted.

Conditions inspect only the receipt of their own step, never the state of the world. One that could read arbitrary files would be an oracle around the kernel ("read /etc/shadow and tell me whether it contains root"), and an unknown condition is dropped rather than counted, so inventing vocabulary gains nothing.

So planning here does the opposite of what it usually does: it makes a run more predictable and less powerful. That is deliberate, and it is the same inversion the unattended ceiling makes β€” a capability that arrives with more rights attached is not a capability this design accepts. Under that ceiling the two compose without knowing about each other: an unattended plan stops at the first step that would need a human, and says so.

web_fetch

returns source; half the web is an empty shell without JavaScript. browse

renders the page in real Chromium and hands back what a reader would see.

What it deliberately cannot do is operate a page β€” no clicking, typing or submitting. A click has no derivable target: "click the third element" cannot be mapped to a resource you could bind a permission to, and a tool without a derivable target is DENY

here by construction. That is the right answer rather than a shortfall β€” an agent that operates strangers' pages has a path to effect that no kernel catches afterwards.

It is also fenced more tightly than a URL check alone can manage. Chromium is pinned to exactly the address guard_url

verified, and nothing else resolves at all:

--host-resolver-rules="MAP * ~NOTFOUND, MAP <host> <ip>"

A redirect to another name goes nowhere, a script pulled from an ad network likewise, and a DNS rebind between check and fetch β€” the classic hole in every filter that inspects only the first name β€” has no effect. The cost is honest: pages that load their content from a CDN under a different name come back incomplete. Incomplete beats uncontrolled.

Every call gets a throwaway profile: no cookies, no signed-in sessions, no history. A page Talos opens sees a factory-fresh browser.

delegate

hands one self-contained question to a second run β€” and that run may only read. No writing, no shell, nothing that needs approval. It uses the same executor, the same kernel and the same identity as its caller; what changes is a fourth ceiling that turns everything except reading into DENY

for as long as it lasts.

The reason is the same one that shapes timed runs. A delegated run is born from model text, not from someone typing. Giving it the rights of the run that started it would make delegation the most convenient way to produce an effect nobody asked for β€” a second source of permission wearing a different name. So delegation buys reach, never power: it is for looking things up without filling the main run's context with the search. What comes back is data, bounded like any tool result, never an instruction.

SOUL.md

carries the agent's name and character. Its first heading is the name β€” change # TALOS

to # ARGUS

, restart, and the agent is renamed everywhere, including the header of the live display. There is no second place where the name lives.

The file also sets the language rule: the agent answers in the language you wrote in.

A plain question gets a plain answer β€” no status chrome. The moment a tool runs, one message starts tracking it and stays afterwards as the receipt:

β—‰ Talos Β· 9s Β· step 2/100
βœ“ shell command  2s
β–Έ write note β€” disk.md

β—‰

agent Β· β—ˆ

thinking Β· ≑

plan announced Β· β–Έ

tool Β· βœ“

done Β· βœ•

failed · ⏸

waiting on you Β· β›’

refused Β· ↩

rolled back. One glyph, one meaning, never in the prose of an answer.

exec.intent

is written before anything happens:

exec.intent β†’ approval.parked β†’ approval.granted β†’ exec.intent β†’ exec.result

A crash mid-action still leaves a trace. Every run carries the id of the token that permitted it. /log

shows the last effects, /undo

rolls back the last file change.

Eighteen, and every one of them passes the same gate. There is no privileged tool and no tool that skips the kernel β€” a tool without a target extractor is DENY

by construction.

run_shell | a command, sandboxed, or refused where no sandbox exists | entity_status | a known name resolved to an operator-configured fixed read-only probe | read_file / write_file / undo_last | ordinary work, with a snapshot behind the write | browse / web_fetch / web_search | render-only, guarded URL, keyless search by default | see_image / grab_frame / hear / speak | a picture, one still out of a video, a recording, a voice | vault_search / vault_get / vault_write_note | a markdown knowledge base, if you point it at one | session_search | what was said in earlier turns | delegate | a sub-run that can only read | ask_operator | the one way it can ask you something on purpose |

Talos ships with no real entity names, hosts or service units. To enable entity-aware status checks, copy the neutral example and replace every placeholder with infrastructure you control:

mkdir -p data
cp examples/entities.json data/entities.json

data/entities.json

is runtime state: Git ignores it, updates preserve it, and Talos reads it as context rather than authority. entity_status

accepts only a configured entity name; the fixed URL or systemd user unit comes from this file and cannot be supplied by the model.

There is no image generation. That is a decision, not a gap: an agent that can produce photographs is a different conversation than one that can only look at them.

In a session β€” the same set in the terminal and in the messenger, through one command centre. A second vocabulary that only worked in one place is exactly the duplication this project has paid for elsewhere.

/stop

/queue

/status

/new

/retry

/background

Β· /pending

/approve

/deny

/allowed

/revoke

Β· /log

/undo

/policy

/autonomy

/tools

/whoami

/version

Β· /usage

/model

/reasoning

/debug

On the command line β€” thirteen, each answering a question an operator actually asks:

chat | a session here; approvals at a terminal | ask "…" | one turn, for scripts and cron | setup Β· doctor Β· config Β· models | set it up, and find out why something won't work | status Β· events Β· why <id> | what it did, and why that was allowed or refused | report Β· review | a record someone else can read Β· what this installation should change | update Β· version | signature checked, tests first |

/policy <path|command>

is a dry run: it calls decide()

and shows the verdict without executing anything. It is the fastest way to understand the kernel.

Small modules on purpose. The gate path (policy.py

, 528 lines) has to be readable in one sitting β€” a gate you cannot read is not a gate.

Module Role
policy.py
the kernel: target derivation, three verdicts
capability.py
tokens: mint, redeem, invalidate
command_floor.py
hardline and dangerous command detection
approval.py / standing.py
one-shot and standing approvals
autonomy.py / trust.py / channel.py
ceilings above the kernel
executor.py / verifier.py / snapshot.py
write-ahead execution, TOCTOU, undo
conductor.py / agent_loop.py / worker.py
orchestration
plan.py / schedule.py / subagent.py
announced sequences, timed runs, delegation β€” all only tighten
web.py / browser.py
the only door out: URL guard, then a resolver cage
reasoner.py / provider.py
pluggable model backends
intelligence.py / evaluation.py
bounded entity context, working state, factual review, adaptive reasoning and trace regression metrics
credentials.py
one route per provider β€” key and address, resolved at call time
sandbox.py
bubblewrap / sandbox-exec ; refuses rather than running unconfined
identity.py / ux.py
name and glyphs
telegram.py / mail.py / whatsapp.py
channels β€” every way in fetches, none listens
vision.py / hearing.py / speech.py / frames.py
reading a picture, hearing a recording, speaking, one still out of a video β€” ordinary READ/WRITE with a target
cli.py / doctor.py / configcli.py / schema.py
the nine subcommands: diagnose, read and change settings β€” the schema decides what may be written, and the allowlist and the network exceptions never are
askcli.py
talos ask β€” one turn from a script, as a channel with no special right
models.py
live model lists, cached on disk, added to the curated catalogue and never replacing it
updater.py
update beside the old tree, both suites in the new one, switch only if green
eventlog.py / memory.py / usage.py
durable log, conversation memory, metering

openat2()

withRESOLVE_BENEATH

instead of realpath checks.An adapter seam for tools. Adding a channel is already just implementing a protocol; adding atoolstill touches three places, one of them inside the kernel (TARGET_EXTRACTORS

). The seam should make a new effect routine without making the kernel optional β€” everything still passes the same gate, the same tokens, the same snapshots, the same receipts.MCP, over that seam and over the official SDK, so a server's tools are proposals like any other β€” never a second place where permission is decided. The hard part is not the transport: a foreign tool arrives without a target extractor, and a tool without one isDENY

by construction. Either a target can be derived from its schema, or the tool is read-only and has none β€” anything else would hand the decision to the server.Ownership as the boundary, not just the floor. While the agent and its config file belong to the same writable user, the floor and the sandbox are code in the same process. A separate user for the model worker would make it a real separation. On the hardened deployment the config already belongs to root and the agent may only read it.

Landed since this list was first written: the sandbox for run_shell

(bubblewrap / sandbox-exec

, see What it does not do), streaming replies, timed runs under a ceiling that makes them weaker than typed ones, announced plans built so that planning tightens a run instead of widening it, read-only delegation, a render-only browser, the media tools, hearing on the machine itself, one still out of a video, keyless search, mail as a second way in, and a command line that grants nothing by being one.

Changes to the kernel (policy.py

, capability.py

, command_floor.py

, approval.py

, standing.py

, autonomy.py

, trust.py

, verifier.py

, executor.py

) need:

pytest

,redteam.py

ande2e.py

green, in that order.Any loosening must add a red-team case proving the boundary next to it still holds.

If you find a way past the kernel, that is the most valuable thing you can contribute β€” and it belongs in the private channel, not in a public issue: see SECURITY.md, which also states the scope and the limits no patch will remove. Everything else β€” a missing tool, a refusal you disagree with, a wrong answer β€” is a normal issue and welcome as one.

MIT. See LICENSE.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @talos 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/talos-ai-agent-super…] indexed:0 read:27min 2026-08-14 Β· β€”