Repository navigation
v0.2.0a20
Pre-release
Pre-release
Security: this release fixes ways an agent guarded through the host hooks could disable or weaken enforcement in 0.2.0a19 and earlier. See GHSA-xh83-347v-qrfw. Upgrade with pip install --pre -U "sponsio==0.2.0a20", then re-run sponsio host install <host> and sponsio doctor.
Added
- Tamper evidence. Every compiled contract gets a stable id. A rule
library change that keeps every loaded contract applies at once and is
recorded asruleset_strengthened. One that removes or changes a
contract is held: the daemon keeps enforcing what it loaded, records
config_drift, and adds a note to the next deny.sponsio rules status | diff | reloadreviews and applies it; a daemon that restarts
onto rules weaker than the last applied set denies that library until
a reload. The daemon also hashes rule files, packs and its own modules
every 2 s, restores a deleted session log from memory
(trace_deleted), and records a call denied by the self-protection
packs astamper_attempt.sponsio daemon run --watchapplies every
change, for working on Sponsio's own rules. sponsio doctorreports the enforcement tier. One line in the
report, andsponsio doctor --tierfor every guarantee of Tier 1, 2 and
3 with pass or fail and the fix. A default install is Tier 1. See
the threat model.sponsio rules verifycompares each host bucket linked under
cloud:indaemon.yamlwith the book the cloud publishes for its
agent, and the daemon does so on its own everyverify_interval_s.
Missing contracts are recorded ascloud_drift. Needs Sponsio Cloud's
/v1/rulebook/verify.- Host config is backed up before every change.
host installand
host uninstallcopy the host's config file to
<file>.sponsio-backup-<timestamp>before changing it, and write
atomically; a symlinked config is written through.host uninstall --restore-backupputs the most recent backup back. - Docs: threat model, daemon
reference, running Sponsio on a
host, and the host hook path in
benchmarks.
Changed
- Hook decisions come from
daemon.yaml, not the environment. The
mode per host, the on-error policy, the policy for tools without rules,
the plugin root and the trace TTL are read from
<state-dir>/daemon.yaml.SPONSIO_GUARD_MODE,SPONSIO_MODE,
SPONSIO_HOOK_ON_ERROR,SPONSIO_UNCONFIGURED,
SPONSIO_SHIELD_TRACE_TTL_HOURSandSPONSIO_PLUGIN_ROOTno longer
affect hooks, and a mode on the hook command or in the OpenClaw
plugin's config is ignored. They still apply to the in-process SDK and
host guard --direct. Migration:sponsio host install <host> --mode observesets observe mode; anenventry in the host's
settings no longer does. - Every hook path fails closed. An unknown host name, empty or
malformed input, a daemon error or an unreadable reply now denies, on
the shim, the daemon,host guardand--direct. The OpenClaw plugin
blocks a call it gets no verdict for unless itsfailOpenoption is
set.on_error: allowstill lets an evaluation error through and each
such call is recorded asfail_open. - Two daemon sockets. State-changing calls (
mode.set,
rules.reload,plugin.append) move to an admin socket beside the
agent socket (sponsio.admin.sock, mode 0600); every admin call is
recorded with the caller's uid, gid and pid read from the kernel.
plugin.appendfrom the agent side becomes a proposal. - A deny names the rule. The reason leads with the contract's own
desc, with the pattern's description after it in parentheses; a
contract without a desc is named by its pattern. host installno longer creates the legacy_host,_host_subagent
and_host_openclawbuckets and reports a bucket it just created as
created.sponsio plugin initstill creates the legacy buckets.- Installed hooks run a shim that talks to the daemon, and fail closed.
sponsio host install claude-code/cursornow write a hook command
that runssponsio_shim.py(standard library only) under the installing
interpreter with-I -S, with the socket, plugin root, state dir and a
2 s deadline pinned on the command line. The shim forwards the
event to the daemon, starts one (sponsio daemon run --detach) when
none answers, and denies with the host's deny body and exit 2 when no
daemon can be reached. It reads no environment variable, so anenv
block in the host settings no longer changes the mode or where the hook
connects. Claude CodePreToolUseentries carry"onFailure": "block"
and"timeout": 10, so a hook that cannot start blocks the call
(Claude Code v2.1.295 or later; older releases ignore the key and the
shim's own exit 2 is the only signal). Reinstall to switch existing
hooks over. sponsio host guardandsponsio plugin guardgo through the daemon
too, with the same fail-closed behaviour;--directkeeps the old
in-process evaluation for debugging. The marketplace plugin's
hooks.jsonaddsonFailureandtimeout.- The OpenClaw plugin talks to the daemon over its socket. It no longer
spawnssponsio plugin guardper tool call.sponsio host install openclawwritessponsio-config.jsonbeside the deployed plugin (socket,
plugin root, state dir, how to start the daemon); the plugin reads
no environment variable, soSPONSIO_GUARD_BIN,SPONSIO_PLUGIN_ROOTand
SPONSIO_GUARD_MODEno longer affect it, and theguardBin/
pluginRoot/guardModemanifest keys are ignored. When no daemon
answers the plugin starts one, and blocks the call if it still cannot be
reached. Reinstall with--forceto replace an already deployed plugin. sponsio doctorchecks the host hooks. "Hook wiring" reports, per
host, a hook whose interpreter or shim is gone, whose socket path cannot
be bound, or that has no daemon and no way to start one, with the command
that fixes it. "Claude Code" warns when the installed release predates
v2.1.295 and so ignoresonFailure.sponsio host installrefuses to
write a hook pinned to a socket path too long to bind.- One daemon per state dir. The daemon holds
<state-dir>/daemon.lock
and never removes a socket another daemon answers on, so hooks that
start it concurrently end with one daemon and one trace. Starts and
restarts are recorded in<state-dir>/audit.jsonl.
Fixed
- A fresh install held every default contract twice. The Claude Code
and Cursor installers wrote the starter library without its bundle
stamp, and the refresh after it kept every shipped contract as the
user's beside a stamped copy. Each check ran every default twice.
Reinstall to clean an affected library: duplicates of a shipped
contract are dropped on upgrade. - Session history is rebuilt in the order calls were admitted. Events
that tied on their timestamp could come back in a different order after
a rebuild, which flippedcooldownand other ordering verdicts. The
daemon now numbers each session's events (seq) and rebuilds by it. - Uninstall leaves nothing behind for a file Sponsio created. A host
config that held only Sponsio's entries is deleted without a backup,
with its earlier Sponsio-only backups, and an emptied skills directory
is removed. Cursor's uninstall no longer deletes ahooks.jsonthat
still holds the user's other keys. - A burst of hooks starting the daemon is not refused. The daemon
listens with the system's full backlog. - Each Claude Code session keeps its own history. Claude Code's hook
payload carriessession_id, notconversation_id, so every Claude Code
session on a machine shared one trace. The history key is now
conversation_id, elsesession_id, prefixed with the host
(claude-code:<session_id>) so two hosts cannot share one. Behaviour
change: count-based rules (rate_limit,idempotent,
bounded_retry) and ordering state now apply per session instead of
accumulating across every session. A Task sub-agent carries its
parent'ssession_idand stays in the parent's history. Cursor traces
move to the prefixed key too; traces recorded under the old key are not
carried over (they expire with the usual TTL). - The installer tests no longer write the developer's own host config.
Host config, skill and credential paths were fixed at import time, so a
test that movedHOMEstill wrote the real~/.claude/settings.json
and~/.cursor/hooks.json. They now resolve per call, and the suite
stops and restores the file if any test writes under the real home.