diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c686a8..f7d51c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,19 @@ remain historical snapshots. Beta does not mean error-free operation.
## [Unreleased]
+### Security
+
+- Raise the transitive `filelock` floor to 4.2.1 and pin 4.2.4 with hashes, replacing
+ vulnerable 3.32.5. No unrelated dependency upgrades. This source change does not
+ replace the existing signed Beta.5 archive or its package-manager installations.
+
+### Documentation
+
+- Document the Beta.5 mission, project, proof and local-check boundaries in the agent
+ guide; clarify Homebrew 7 requirements, profile ownership and update commands.
+- Separate public pull-request workflows from sanitized private exports and document
+ the explicit OSV scan of all four shipped lockfiles.
+
## [0.20.0-beta.5] — 2026-10-21
### Security
diff --git a/CLAUDE.md b/CLAUDE.md
index 727689e..ce5cb31 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -14,13 +14,39 @@ preferences live in `USER.md`. All three are operator-owned prompt state and rel
|---|---|
| Gate path | `policy.py`, **993 lines** — has to stay readable in one sitting |
| Tools | **35**, every one gated |
-| Suites | **3355** collected tests · **269** adversarial · 44 end-to-end |
+| Suites | **3356** collected tests · **269** adversarial · 44 end-to-end |
| Home | · docs at `/docs/` |
| Repository | `talos-kernel/talos` is the public source tree |
-⚠️ **`public` is blocked for pushing.** Its push url is a deliberate dead end — the
-published state is only ever produced by `scripts/sync-public.sh` into a separate clone.
-A direct push from here would carry 108 commits and a real author address across.
+**Check the checkout before publishing.** Contributors to the public repository use
+a branch and pull request. A private development checkout must publish only a reviewed,
+sanitized tree through `scripts/sync-public.sh` into a separate public clone; never push
+private history. Inspect the remotes and current branch instead of assuming their names.
+
+## Current capabilities and installation context
+
+Beta.5 introduced [missions and projects](docs/missions.md), `/continue`, outcome
+receipts and a loopback local-model check. Mission templates and saved project context
+grant no permission: each action still passes the kernel. Mailbox missions read mail
+and prepare local drafts; they do not send mail. A recorded successful action is
+historical evidence, not proof that an artifact remains unchanged. Receipts omit prompts
+and result bodies, but artifact paths can still need redaction before sharing.
+
+`talos local-check` tests a loopback endpoint without user content and refuses redirects;
+it cannot prove that another process never uses the network. See the mission guide for
+the exact commands and limitations.
+
+[Homebrew](docs/homebrew.md) requires Homebrew 7+, Apple Silicon and macOS 14+. The tap
+owns the code; private state lives in `~/.talos/homebrew` (directories 0700, newly created
+identity/configuration files 0600). A shared or symlinked profile is refused. Explicit
+`TALOS_PROFILE_DIR` overrides must be absolute. Existing installations are not imported
+automatically; do not overwrite operator state with repository templates. Homebrew
+installations update through `brew upgrade talos-kernel/tap/talos`; `talos update`
+refuses them. Source installations retain their own profile and update path.
+
+The source branch can contain fixes newer than the latest signed release. Check
+`CHANGELOG.md`, the release tag and `site/status.json` before claiming a fix is shipped;
+a source commit does not change an existing signed archive.
## The rule everything rests on
@@ -274,7 +300,7 @@ properties covered by `test_fallback_control.py` and `test_runtime_failure_repai
```bash
python3 -m venv .venv && . .venv/bin/activate && pip install --require-hashes -r requirements.lock -r requirements-dev.lock
-python -m pytest tests/ -q # 3355 collected tests; platform-specific skips are explicit
+python -m pytest tests/ -q # 3356 collected tests; platform-specific skips are explicit
python redteam.py # 269 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
@@ -287,6 +313,8 @@ python -m talos status # what the event log says it did last
python -m talos health # runs, errors, schedules, last anchor — no network, exit 1 on a broken chain
python -m talos report # the event log as a record a third party can read
python -m talos review # what this installation should change — writes nothing
+python -m talos proof --format json # recorded outcomes; inspect paths before sharing
+python -m talos local-check --help # options for a loopback-only model check
python -m talos ask "…" # one turn, no chat — cli: must be in the allowlist
python -m talos chat # a session here; the ceiling hangs on the tty, not the command
python -m talos events # what happened — read-only, filterable
@@ -306,9 +334,18 @@ mock HTTP endpoints in new tests use reserved example domains, not private IP li
## Dependencies: intent vs. what gets installed
-Scan the shipped pins explicitly with OSV: `osv-scanner scan source --no-resolve
---lockfile requirements.txt:requirements.lock --lockfile requirements.txt:requirements-dev.lock`.
+Scan all four lockfiles explicitly with OSV:
+
+```sh
+osv-scanner scan source --no-resolve \
+ -L requirements.txt:requirements.lock \
+ -L requirements.txt:requirements-dev.lock \
+ -L requirements.txt:requirements-verifier.lock \
+ -L requirements.txt:deploy/computer-requirements.lock
+```
+
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.
`requirements*.txt` is the statement of intent (ranges). `requirements*.lock` is what
gets installed: every version pinned, every file hashed, resolved for every platform
@@ -345,20 +382,11 @@ covers the packages only through it. `pip` is never upgraded unpinned first.
that did not survive an update makes a deliberately configured install answer with the
shipped default. The fallback stays, but it leaves a trace — and a real deployment
names its model in `talos.env` so the fallback is *its* model, not this repo's.
-9. **Two repositories, and the remotes say which is which.** In this working tree
- `private` carries the full history and the real author address; `public` is
- `talos-kernel/talos`, an own clean history from zero. Run `git remote -v` for the
- addresses — ⚠️ they are deliberately **not** written out here, because this file is
- published with the tree, and the private one has no business being in it. The
- deployment target is neither: it is a running instance fed by `rsync`, and a third
- remote for it no longer exists.
- ⚠️ Moved into the organisation on 2026-08-06 by pushing fresh, **not** by transfer:
- a transfer leaves a permanent redirect from the personal account, and with zero stars
- it would have bought nothing. The old repo was deleted, so no redirect exists.
- ⚠️ **`public` is blocked
- for pushing** — its push url is a deliberate dead end. The public state is only ever
- produced by `scripts/sync-public.sh` into a separate clone; a direct push from here
- would carry the whole private history across.
+9. **Private and public history stay separate.** Public contributors work on the
+ public tree through branches and pull requests. Private development uses the
+ sanitized export described above. Never merge a private branch into the public
+ repository or publish local machine paths, operator identities or private remotes.
+ A running operator installation is a separate deployment, not a Git remote.
10. **An operator installation is not this repo.** The instance on the operator's machine has
its own `SOUL.md` (its first heading is the agent's *name*), its own `CLAUDE.md`,
its own env file and its own `data/`. Sync the **package** (`talos/` → `talos/`), never
diff --git a/README.md b/README.md
index 4095c94..2906b5f 100644
--- a/README.md
+++ b/README.md
@@ -21,10 +21,10 @@
-
-
+
@@ -54,7 +54,7 @@ operation or a substitute for reviewing permissions and keeping backups.
[Watch a recorded session](https://talos-agent.ch/console.html) ·
[Install with Pinokio](https://github.com/talos-kernel/talos-pinokio)
-Apple Silicon on macOS 14+: [install the CLI with Homebrew](docs/homebrew.md).
+Apple Silicon on macOS 14+ with Homebrew 7+: [install the CLI with Homebrew](docs/homebrew.md).
**New to Talos?** [Try three small, checkable tasks](docs/first-task.md): turn notes into
a handover, save and read back a local note, and compare release notes without inventing
@@ -67,6 +67,10 @@ checks. Resume saved project context with `/continue` and inspect recorded outco
with `talos proof`. Templates grant no extra permissions; external sends and protected
actions retain their existing gates.
+**Source versus release:** `main` can contain fixes newer than the signed installer.
+The [unreleased changelog](CHANGELOG.md#unreleased) records the `filelock` security
+update; the existing Beta.5 archive and Homebrew package retain their published pins.
+
## Is Talos right for you?
- **Run local models:** connect Ollama on hardware you control, or use a configured cloud provider.
diff --git a/docs/homebrew.md b/docs/homebrew.md
index 172d41d..0ba37f8 100644
--- a/docs/homebrew.md
+++ b/docs/homebrew.md
@@ -4,6 +4,9 @@ Talos's own tap packages the beta CLI, not a notarized desktop application or a
Homebrew/core formula. Intel Macs and Linux are not supported by this formula;
use the signed installer for the other supported environments.
+Homebrew 7 or newer is required. Run `brew update` before installing with an older
+Homebrew installation.
+
```sh
brew install talos-kernel/tap/talos
talos setup terminal
diff --git a/requirements.lock b/requirements.lock
index bc011ba..b151492 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -413,10 +413,12 @@ ddgs==9.16.0 \
faster-whisper==1.2.1 \
--hash=sha256:79a66ad50688c0b794dd501dc340a736992a6342f7f95e5811be60b5224a26a7
# via -r requirements.txt
-filelock==3.32.5 \
- --hash=sha256:142cd9fa77a872c5e78c62329a0d15278fadc686eb89e760017968961a4fd6b2 \
- --hash=sha256:f6a6a28f743f9b95ce19db5abe0f376f75eb56517dff21e1a4751e2657d3e83d
- # via huggingface-hub
+filelock==4.2.4 \
+ --hash=sha256:301241438c57936ef93f4243a94f2559022268bfcf9b375a9a72eb0f8db8358e \
+ --hash=sha256:c6d8663535e1650ee430507b4ffbac7b464dd64b7647ea0112a9f406499d163d
+ # via
+ # -r requirements.txt
+ # huggingface-hub
flatbuffers==25.12.19 \
--hash=sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4
# via onnxruntime
diff --git a/requirements.txt b/requirements.txt
index 8980752..e75c6f5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,3 +15,4 @@ anyio>=4.14.2 # transitiv ueber httpx. Untergrenze 4.14.2: alles
# GHSA-5p39-cfhj-2xmp (CVSS 6.8). Dieselbe Regel wie bei idna.
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.
diff --git a/site/console.html b/site/console.html
index 22d9b20..f9d77d0 100644
--- a/site/console.html
+++ b/site/console.html
@@ -378,7 +378,7 @@
Witness one deed from door to ledger.
This candidate, measured
-
3355automated tests
+
3356automated tests
269/269adversarial
993lines · gate
35gated tools
@@ -554,7 +554,7 @@
Numbers this candidate must ship.
0.20.0-beta.5
beta release
public installer v0.20.0-beta.5
-
3355
automated tests, collected by pytest
+
3356
automated tests, collected by pytest
269
adversarial cases, all behaving
993
lines in policy.py, readable in one sitting
35
tools, every one through the gate
diff --git a/site/docs/index.html b/site/docs/index.html
index 4dd9b6d..90548ce 100644
--- a/site/docs/index.html
+++ b/site/docs/index.html
@@ -244,7 +244,7 @@
Talos AI agent: the field manual
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.
-
3355automated tests
+
3356automated tests
269/269adversarial
993gate lines
35gated tools
diff --git a/site/index.html b/site/index.html
index 2e85b01..9414066 100644
--- a/site/index.html
+++ b/site/index.html
@@ -638,8 +638,8 @@
The bounds are named.
Measured, with the path stated
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.
-
Release snapshot: 0.20.0-beta.5. Counts describe its test suite, not speed or real-world success rates.
-
0
automated tests collected from the source tree
+
Source tree based on 0.20.0-beta.5, including unreleased fixes. Counts describe the source test suite, not speed or real-world success rates; signed release contents follow their tag.
+
0
automated tests collected from the source tree
0
adversarial cases trying to pass the kernel
0
opt-in end-to-end cases against a real model
0
lines in the file that decides every verdict
diff --git a/site/llms.txt b/site/llms.txt
index 3915a11..a5f89d5 100644
--- a/site/llms.txt
+++ b/site/llms.txt
@@ -6,7 +6,7 @@
- Permission kernel: 993 lines of Python, the only code that decides
- 35 tools, all gated; unattended runs cannot escalate
-- 3355 tests collected in the source tree; 269/269 red-team cases passed in source verification. Release contents follow their own signed tag.
+- 3356 tests collected in the source tree; 269/269 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
diff --git a/site/redteam/index.html b/site/redteam/index.html
index 0859d50..f14fe9c 100644
--- a/site/redteam/index.html
+++ b/site/redteam/index.html
@@ -224,7 +224,7 @@
269 ways to break an agent.
25attack surfaces
993kernel lines
35gated tools
-
3355automated tests
+
3356automated tests
0passes sold as safety
diff --git a/site/registry/index.html b/site/registry/index.html
index b84eb8e..fcbfd74 100644
--- a/site/registry/index.html
+++ b/site/registry/index.html
@@ -239,7 +239,7 @@
The MCP registry
guide with the full protocol is
docs/claude-worker.md.
A curated starting point ships as
examples/mcp-servers.json.
-
3355automated tests
+
3356automated tests
269/269adversarial
993gate lines
35gated tools
diff --git a/site/setup/index.html b/site/setup/index.html
index 49625f3..048b679 100644
--- a/site/setup/index.html
+++ b/site/setup/index.html
@@ -163,7 +163,7 @@
This address moved.
Return to the keep
-
3355automated tests
+
3356automated tests
269/269adversarial
993gate lines
35gated tools
diff --git a/site/status.json b/site/status.json
index 01fe1d3..0c95218 100644
--- a/site/status.json
+++ b/site/status.json
@@ -1,8 +1,9 @@
{
"version": "0.20.0-beta.5",
"release_state": "released",
+ "source_state": "unreleased_changes",
"installer_version": "0.20.0-beta.5",
- "tests": 3355,
+ "tests": 3356,
"redteam_faelle": 269,
"kernel_zeilen": 993,
"werkzeuge": 35,
diff --git a/site/vergleich/index.html b/site/vergleich/index.html
index 1249a48..7ae81cf 100644
--- a/site/vergleich/index.html
+++ b/site/vergleich/index.html
@@ -290,7 +290,7 @@
The others can do more.
35tools here, every one gated
100+skills there · counted honestly
-
3355automated tests
+
3356automated tests
269/269adversarial cases
993gate lines
0inbound ports
diff --git a/tests/test_requirements_lock.py b/tests/test_requirements_lock.py
index 2017c39..4792f3c 100644
--- a/tests/test_requirements_lock.py
+++ b/tests/test_requirements_lock.py
@@ -127,6 +127,18 @@ def test_pypdf_lock_includes_denial_of_service_security_fixes() -> None:
for version, _ in _pins(RUNTIME[1])["pypdf"])
+def test_filelock_floor_and_lock_exclude_known_vulnerable_versions() -> None:
+ """The transcription cache must keep the October file-locking fixes."""
+ requirement = next((r for r in _declared(RUNTIME[0])
+ if _canonical(r.name) == "filelock"), None)
+ assert requirement is not None, "filelock needs an explicit security floor"
+ assert not requirement.specifier.contains(Version("3.32.5"))
+ assert not requirement.specifier.contains(Version("4.2.0"))
+ assert requirement.specifier.contains(Version("4.2.1"))
+ assert all(version >= Version("4.2.1")
+ for version, _ in _pins(RUNTIME[1])["filelock"])
+
+
@pytest.mark.parametrize("lock", [RUNTIME[1], DEV[1]], ids=["runtime", "dev"])
def test_the_lock_carries_no_machine_path(lock: Path) -> None:
"""Der Kopf zitiert die Kommandozeile — erzeugt mit relativen Pfaden, oder gar nicht."""