Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
The 0.20.0 beta line follows the frozen operator contract; earlier alpha releases
remain historical snapshots. Beta does not mean error-free operation.

## [0.20.0-beta.8] — 2026-10-25

### Security

- Hash-lock pip 26.2.2 in the runtime and temporary signature-verifier environments.
Fresh virtual environments previously retained the pip version bundled with the
operator's Python, which could remain vulnerable even when application locks were clean.
Installation and update now replace that seed through the signed/pinned locks;
no unpinned self-upgrade or system-Python modification is introduced.
- Installer and updater pip subprocesses ignore user configuration and Python import overrides, preventing stale duplicate metadata or redirected installs. Older updaters with custom pip settings can upgrade once using `PIP_CONFIG_FILE=/dev/null talos update`.
- Retains all Beta.7 task-recovery and monitoring-guidance fixes.

## [0.20.0-beta.7] — 2026-10-25

### Fixed
Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ preferences live in `USER.md`. All three are operator-owned prompt state and rel
|---|---|
| Gate path | `policy.py`, **996 lines** — has to stay readable in one sitting |
| Tools | **37**, every one gated |
| Suites | **3437** collected tests · **277** adversarial · 44 end-to-end |
| Suites | **3438** collected tests · **277** adversarial · 44 end-to-end |
| Home | <https://talos-agent.ch> · docs at `/docs/` |
| Repository | `talos-kernel/talos` is the public source tree |

Expand Down Expand Up @@ -308,7 +308,7 @@ repairs; corrections never replay an effect or grant permission. Keep those prop
```bash
python3 -m venv .venv && . .venv/bin/activate && pip install --require-hashes -r requirements.lock -r requirements-dev.lock

python -m pytest tests/ -q # 3437 collected tests; platform-specific skips are explicit
python -m pytest tests/ -q # 3438 collected tests; platform-specific skips are explicit
python redteam.py # 277 adversarial cases — mandatory for any kernel change
python e2e.py # 44 cases against a real model (costs tokens and time)
python -m talos --once # single cycle, for diagnosis
Expand Down Expand Up @@ -352,6 +352,9 @@ osv-scanner scan source --no-resolve \
-L requirements.txt:deploy/computer-requirements.lock
```

The runtime and verifier locks include pip itself. Audit actual installed distributions as well
as application locks: the interpreter can otherwise contribute an outdated bootstrap tool.

Automatic discovery scans the range files and misses these nonstandard lock names.
Record the scan date and result; a previous clean scan does not cover new advisories.

Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
</p>

<p align="center">
<!-- ⚠️ Bewusst „tests“, nicht „passing“: die Zahl kommt aus dem Einsammeln (3437).
<!-- ⚠️ Bewusst „tests“, nicht „passing“: die Zahl kommt aus dem Einsammeln (3438).
Plattformabhaengige Sandbox- und Repository-Pruefungen koennen uebersprungen werden;
`test_site_claims` prueft deshalb die gesammelte Zahl statt ein Umgebungsresultat. -->
<img src="https://img.shields.io/badge/tests-3437-2e7d32.svg" alt="Tests">
<img src="https://img.shields.io/badge/tests-3438-2e7d32.svg" alt="Tests">
<img src="https://img.shields.io/badge/red%20team-277%2F277-2e7d32.svg" alt="Red team">
<img src="https://img.shields.io/badge/gate%20path-996%20lines-8a4318.svg" alt="Gate path">
<img src="https://img.shields.io/badge/tools-37%20gated-8a4318.svg" alt="Tools">
Expand Down Expand Up @@ -74,6 +74,8 @@ security update. See the [changelog](CHANGELOG.md#0200-beta6--2026-10-24).
**Source versus release:** `main` can contain newer changes. Signed installers and
package managers follow their pinned release; older archives remain immutable.

Beta.8 also pins pip 26.2.2 with hashes, including the temporary verifier, and isolates pip from per-user configuration. When upgrading from Beta.7 or older with custom pip settings, use `PIP_CONFIG_FILE=/dev/null talos update` once; those older updaters still inherit pip configuration.

## Is Talos right for you?

- **Run local models:** connect Ollama on hardware you control, or use a configured cloud provider.
Expand Down Expand Up @@ -379,7 +381,7 @@ come from `stdin` instead of `getUpdates`.
```
$ talos chat

◉ Talos / 0.20.0-beta.7
◉ Talos / 0.20.0-beta.8
Your terminal. Your rules.
──────────────────────────────────────────────────────────────────────────
model claude-cli/claude-opus-4-1
Expand Down
4 changes: 2 additions & 2 deletions macos/app-version.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "0.3.6",
"build": "12"
"version": "0.3.7",
"build": "13"
}
3 changes: 3 additions & 0 deletions requirements-verifier.lock
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ cryptography==50.0.1 \
--hash=sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80 \
--hash=sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558 \
--hash=sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef
pip==26.2.2 \
--hash=sha256:3a8e5c800492787f33a4114ef54444546c340df428974ebba17e0fd47e11af2f \
--hash=sha256:fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70
pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \
--hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \
--hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992
4 changes: 4 additions & 0 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,10 @@ packaging==26.3 \
# via
# huggingface-hub
# onnxruntime
pip==26.2.2 \
--hash=sha256:3a8e5c800492787f33a4114ef54444546c340df428974ebba17e0fd47e11af2f \
--hash=sha256:fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70
# via -r requirements.txt
primp==2.0.0 \
--hash=sha256:0776fd400adcc940e47bb70896fb474f6607ffa545f5f7107ccda8d055fa02c9 \
--hash=sha256:0ea78a7a2fc5d48a825fa73182911b956bcb58940297cbd60958963ec30375d4 \
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ anyio>=4.14.2 # transitiv ueber httpx. Untergrenze 4.14.2: alles
certifi>=2026.10.19 # transitiv ueber requests/httpx. Untergrenze schliesst
# PYSEC-2026-4320 / GHSA-6q48-6c7w-9gw2 (CVSS 9.1).
filelock>=4.2.1 # Transitive transcription-cache dependency: October locking security fixes.
pip>=26.2.0 # Hash-lock the installer itself; do not inherit a vulnerable Python seed.
14 changes: 13 additions & 1 deletion scripts/beta-clean-install.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from __future__ import annotations

import argparse
import json
import re
import os
import stat
import subprocess
Expand Down Expand Up @@ -50,14 +52,24 @@ def main() -> int:
if version.returncode or version.stdout.strip() != args.current:
print("Installed version read-back failed", file=sys.stderr)
return 1
expected_pip = re.search(r"^pip==([^\s\\]+)", (ROOT / "requirements.lock").read_text(), re.M).group(1)
pip_probe = subprocess.run(
[str(prefix / ".venv/bin/python"), "-I", "-c",
"import json,pip,importlib.metadata as m;print(json.dumps({"
"'module':pip.__version__,'distributions':[d.version for d in m.distributions() "
"if d.metadata['Name'].lower()=='pip']}))"],
capture_output=True, text=True, check=True)
if json.loads(pip_probe.stdout) != {"module": expected_pip, "distributions": [expected_pip]}:
print("Installed pip differs from its lock or has stale duplicate metadata", file=sys.stderr)
return 1
config = prefix / "talos.env"
if not config.is_file() or stat.S_IMODE(config.stat().st_mode) != 0o600:
print("Installed configuration mode read-back failed", file=sys.stderr)
return 1
if not (bin_dir / "talos").is_symlink() or not (prefix / "data").is_dir():
print("Installed command or data directory missing", file=sys.stderr)
return 1
print(f"Clean install: {args.current}, suites passed, config 0600, command and data read back")
print(f"Clean install: {args.current}, suites passed, config 0600, command and data read back; pip {expected_pip} unique")
return 0


Expand Down
2 changes: 1 addition & 1 deletion scripts/beta-upgrade-rehearsal.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def _extract_old(payload: bytes, destination: Path) -> None:
def _python(code: str, tree: Path, *args: str) -> subprocess.CompletedProcess[str]:
environment = {key: os.environ[key] for key in
("PATH", "HOME", "TMPDIR", "LANG", "LC_ALL", "SSL_CERT_FILE",
"REQUESTS_CA_BUNDLE")
"REQUESTS_CA_BUNDLE", "PIP_CONFIG_FILE")
if key in os.environ}
environment.update({"PIP_NO_INPUT": "1", "PIP_DISABLE_PIP_VERSION_CHECK": "1",
"PYTHONNOUSERSITE": "1"})
Expand Down
2 changes: 1 addition & 1 deletion scripts/sync-verifier-lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


def verifier_lock(runtime: str) -> str:
wanted = {"cryptography", "cffi", "pycparser"}
wanted = {"cryptography", "cffi", "pycparser", "pip"}
blocks = re.split(r"(?=^[a-zA-Z0-9][a-zA-Z0-9_.-]*==)", runtime, flags=re.M)
selected = []
found = set()
Expand Down
10 changes: 5 additions & 5 deletions site/console.html
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@

<header class="hero" id="top">
<div class="wrap">
<p class="sys">talos 0.20.0-beta.7 · operator console · MIT</p>
<p class="sys">talos 0.20.0-beta.8 · operator console · MIT</p>
<span class="rec-chip">recorded · unfurled · no live wire</span>
<h1 id="main-content" tabindex="-1">Witness one deed from door to ledger.</h1>
<p class="lede">This hall unfurls one true session, recorded on the working machine
Expand All @@ -378,7 +378,7 @@ <h1 id="main-content" tabindex="-1">Witness one deed from door to ledger.</h1>
<a class="btn ghost" href="#measured">This candidate, measured</a>
</div>
<div class="meter-row">
<span><b>3437</b>automated tests</span>
<span><b>3438</b>automated tests</span>
<span><b>277/277</b>adversarial</span>
<span><b>996</b>lines · gate</span>
<span><b>37</b>gated tools</span>
Expand Down Expand Up @@ -553,12 +553,12 @@ <h2 class="rv">Numbers this candidate must ship.</h2>
embedded below. Either way the digits come from a count, not from memory.</p>

<div class="stats" id="releaseStats">
<div class="stat rv"><div class="n" data-f="version">0.20.0-beta.7</div><div class="l">beta release</div><div class="sub-l" data-f="installer_version">public installer v0.20.0-beta.7</div></div>
<div class="stat rv"><div class="n" data-f="tests">3437</div><div class="l">automated tests, collected by pytest</div></div>
<div class="stat rv"><div class="n" data-f="version">0.20.0-beta.8</div><div class="l">beta release</div><div class="sub-l" data-f="installer_version">public installer v0.20.0-beta.8</div></div>
<div class="stat rv"><div class="n" data-f="tests">3438</div><div class="l">automated tests, collected by pytest</div></div>
<div class="stat rv"><div class="n" data-f="redteam_faelle">277</div><div class="l">adversarial cases, all behaving</div></div>
<div class="stat rv"><div class="n" data-f="kernel_zeilen">996</div><div class="l">lines in policy.py, readable in one sitting</div></div>
<div class="stat rv"><div class="n" data-f="werkzeuge">37</div><div class="l">tools, every one through the gate</div></div>
<div class="stat rv"><div class="n" data-f="release_ref">v0.20.0-beta.7</div><div class="l">release tag</div></div>
<div class="stat rv"><div class="n" data-f="release_ref">v0.20.0-beta.8</div><div class="l">release tag</div></div>
</div>

<div class="measure-note rv">
Expand Down
4 changes: 2 additions & 2 deletions site/docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ <h1 id="main-content" tabindex="-1">Talos AI agent: the field manual</h1>
<p class="lede">That one decision shapes the rest of this manual. It is why the
command list is short, and why a few obvious conveniences are missing on purpose.</p>
<div class="spec-row">
<div class="spec"><b>3437</b>automated tests</div>
<div class="spec"><b>3438</b>automated tests</div>
<div class="spec"><b>277/277</b>adversarial</div>
<div class="spec"><b>996</b>gate lines</div>
<div class="spec"><b>37</b>gated tools</div>
Expand Down Expand Up @@ -407,7 +407,7 @@ <h2><span class="n">Section 04</span>Run it</h2>
<section id="session" class="rv">
<h2><span class="n">Section 05</span>The terminal session</h2>
<pre>$ talos chat
Talos 0.20.0-beta.7 · anthropic-api/claude-opus-4-8 · autonomy 3
Talos 0.20.0-beta.8 · anthropic-api/claude-opus-4-8 · autonomy 3
speaking as cli:1000 · approvals possible · you are at a terminal
/help for commands, `exit` to leave

Expand Down
4 changes: 2 additions & 2 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ <h2 class="rv">Measured, with the path stated</h2>
<p class="sub rv">None of this asks for belief. The installer runs the unit and
adversarial suites in front of you; the real-model cases are a separate opt-in E2E run.</p>
<p class="source-caption">Beta.6 source and signed release. Counts describe the source test suite, not speed or real-world success rates; signed release contents follow their tag.</p><div class="stats">
<div class="stat rv"><div class="n cnt" data-n="3437">0</div><div class="l">automated tests collected from the source tree</div></div>
<div class="stat rv"><div class="n cnt" data-n="3438">0</div><div class="l">automated tests collected from the source tree</div></div>
<div class="stat rv"><div class="n cnt" data-n="277">0</div><div class="l">adversarial cases trying to pass the kernel</div></div>
<div class="stat rv"><div class="n cnt" data-n="44">0</div><div class="l">opt-in end-to-end cases against a real model</div></div>
<div class="stat rv"><div class="n cnt" data-n="996">0</div><div class="l">lines in the file that decides every verdict</div></div>
Expand Down Expand Up @@ -764,7 +764,7 @@ <h2 class="rv">Talos AI agent: your questions</h2>
<div class="wrap">
<p class="sec-tag rv">raise the keep</p>
<h2 class="rv">The command remains yours</h2>
<p class="sub rv">Beta release 0.20.0-beta.7. Tested on Python 3.11 · 3.13, Linux and macOS, including Raspberry Pi.
<p class="sub rv">Beta release 0.20.0-beta.8. Tested on Python 3.11 · 3.13, Linux and macOS, including Raspberry Pi.
Bring your own model: a supported CLI, configured API provider or local Ollama.
The installer runs the unit and adversarial suites in front of you, then <strong>starts nothing.</strong></p>
<div class="term rv">
Expand Down
14 changes: 9 additions & 5 deletions site/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

set -euo pipefail

VERSION="0.20.0-beta.7"
VERSION="0.20.0-beta.8"
BASE="${TALOS_BASE:-https://talos-agent.ch}"
TARBALL="${BASE}/dist/talos-${VERSION}.tar.gz"
PREFIX="${TALOS_PREFIX:-$HOME/talos}"
Expand Down Expand Up @@ -326,12 +326,15 @@ cryptography==50.0.1 \
--hash=sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80 \
--hash=sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558 \
--hash=sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef
pip==26.2.2 \
--hash=sha256:3a8e5c800492787f33a4114ef54444546c340df428974ebba17e0fd47e11af2f \
--hash=sha256:fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70
pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \
--hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \
--hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992
VERIFIER_LOCK
# END PINNED VERIFIER
spin "verifier deps" "$TMP/verify/bin/python" -m pip install --quiet --require-hashes --only-binary=:all: --no-deps -r "$TMP/verifier.lock" \
spin "verifier deps" "$TMP/verify/bin/python" -I -m pip --isolated install --quiet --require-hashes --only-binary=:all: --no-deps -r "$TMP/verifier.lock" \
|| die "could not install the signature verifier — an install is not the place to skip this."
"$TMP/verify/bin/python" - "$TMP/talos.tar.gz" "$TMP/sig" <<'VERIFY' || die "the release signature does not match this archive. Nothing was unpacked, nothing was changed."
import base64, pathlib, sys
Expand Down Expand Up @@ -362,17 +365,18 @@ VPY="$PREFIX/.venv/bin/python"
# Every version pinned, every file hashed, resolved for every platform when the
# release was cut — installed under --require-hashes, so the signature checked
# above also covers what pip fetches now. pip itself is not upgraded first: that
# would be one unpinned download ahead of all the pinned ones.
# would be one unpinned download ahead of all the pinned ones. The lock includes pip.
# Isolated mode ignores per-user pip settings (such as ignore-installed/target).
[ -f "$PREFIX/requirements.lock" ] \
|| die "this archive ships no requirements.lock — refusing to install unpinned dependencies."
spin "dependencies" "$VPY" -m pip install --quiet --require-hashes -r "$PREFIX/requirements.lock" \
spin "dependencies" "$VPY" -I -m pip --isolated install --quiet --require-hashes -r "$PREFIX/requirements.lock" \
|| die "dependencies failed — a hash mismatch here means a package did not arrive as released."
# The proofs below need a test runner. It is a separate lock because the agent
# itself never imports it — installing it here is the price of not asking you
# to take the claims on faith.
[ -f "$PREFIX/requirements-dev.lock" ] \
|| die "this archive ships no requirements-dev.lock — the proofs below would run unpinned."
spin "test runner" "$VPY" -m pip install --quiet --require-hashes -r "$PREFIX/requirements-dev.lock" \
spin "test runner" "$VPY" -I -m pip --isolated install --quiet --require-hashes -r "$PREFIX/requirements-dev.lock" \
|| die "could not install the test runner — the proofs below would be skipped."
ok "$PREFIX/.venv — no system packages touched"

Expand Down
2 changes: 1 addition & 1 deletion site/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

- Permission kernel: 996 lines of Python, the only code that decides
- 37 tools, all gated; unattended runs cannot escalate
- 3437 tests collected in the source tree; 277/277 red-team cases passed in source verification. Release contents follow their own signed tag.
- 3438 tests collected in the source tree; 277/277 red-team cases passed in source verification. Release contents follow their own signed tag.
- MCP servers run only inside a UID-separated worker, from an operator-owned registry — no marketplace, no third-party code in the agent process
- Channels: Telegram, mail (IMAP pull), CLI, WhatsApp via an operator-controlled broker — every channel fetches; no inbound port, no webhook
- Updates: signed tarballs, verified before anything is unpacked; dependencies pinned and hashed, installed with --require-hashes
Expand Down
2 changes: 1 addition & 1 deletion site/redteam/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ <h1 id="main-content" tabindex="-1">277 ways to break an agent.</h1>
<div class="spec"><b class="red">25</b>attack surfaces</div>
<div class="spec"><b>996</b>kernel lines</div>
<div class="spec"><b>37</b>gated tools</div>
<div class="spec"><b>3437</b>automated tests</div>
<div class="spec"><b>3438</b>automated tests</div>
<div class="spec"><b class="red">0</b>passes sold as safety</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion site/registry/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ <h1 id="main-content" tabindex="-1">The MCP registry</h1>
guide with the full protocol is <a href="https://github.com/talos-kernel/talos/blob/master/docs/claude-worker.md">docs/claude-worker.md</a>.
A curated starting point ships as <code>examples/mcp-servers.json</code>.</p>
<div class="spec-row">
<div class="spec"><b>3437</b>automated tests</div>
<div class="spec"><b>3438</b>automated tests</div>
<div class="spec"><b>277/277</b>adversarial</div>
<div class="spec"><b>996</b>gate lines</div>
<div class="spec"><b>37</b>gated tools</div>
Expand Down
2 changes: 1 addition & 1 deletion site/setup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ <h1 id="main-content" tabindex="-1" class="rv">This address moved.</h1>
<a class="btn ghost" href="/">Return to the keep</a>
</div>
<div class="spec-row rv">
<div class="spec"><b>3437</b>automated tests</div>
<div class="spec"><b>3438</b>automated tests</div>
<div class="spec"><b>277/277</b>adversarial</div>
<div class="spec"><b>996</b>gate lines</div>
<div class="spec"><b>37</b>gated tools</div>
Expand Down
Loading
Loading