Repository navigation
Actualis 0.3.0
Upgrading from 0.2.2: what can change a gate
The same transcripts can give a different exit code than they did on 0.2.2.
Each of these is listed in full below.
--fail-on highcan newly exit 3 on transcripts 0.2.2 passed:- new
highremote-execshapes (bash <(curl …),source <(…),
sh -c "$(curl …)", dequotedcu''rl … | s''h,curl … |& sh, and a
shell at any later stage of the pipeline); - newly counted credentials (passwords passed as options, short
PASSWORDvariables, short URL and scp passwords, long password-less
userinfo, unprefixedAuthorizationheaders), which arehigh; - the new
audit-configfinding, which ishighand cannot be suppressed.
- new
--fail-on anycan newly exit 3 on the newmediumoversized-command
flag.- Coach findings move with cost. The new pricing changes spend figures,
and spend-based findings (AF001, AF007, AF009, AF010) can appear or vanish. - Only when you opt in:
--network-strictaddsnetwork-unaskedfindings
(any);--iocaddsnetwork-ioc(high) andnetwork-ioc-unresolved
(any). - Counts rise (
secret_exposures,secret_projects,
unreadable_commands), andreport_sha256is not comparable with 0.2.2. Take
a fresh--diffbaseline.
Changed
-
Faster. The shell-audit rules and unreadable shapes check a hand-written
literal prefilter before each regex (results are identical; a test compares
both paths over every test string and 5,000 generated commands), and
--networkwithout--fail-onskips the audit and secret ranking it never
shows. On a 1,169-file corpus--networkwent from 43 s to about 6 s and a
full run from 44 s to about 21 s. -
New
highremote-exec detections. Besidescurl … | sh, the shell
audit now flagsbash <(curl …),source <(…)and. <(…),
sh -c "$(curl …)"(any shell, any-ccluster such as-lc), a dequoted
cu''rl … | s''horc\url … | busybox sh,curl … |& sh, and a shell at
any later stage of the pipeline (curl … | tee f | sh). Each command is
flagged once. These can newly fail--fail-on high(exit 3) on transcripts
0.2.2 passed. -
Location-based ids for person-chosen passwords. A password passed as an
option (curl -u user:PASS,--user=,-U,wget --password=,docker login -p), and every credential first counted in this release (see Fixed),
gets an id derived from where it appears (sha256("opt:" + program + ":" + option + ":" + user)[:8]and the like), not a hash of the value, so a
published id cannot confirm a guessed password. Credentials counted in 0.2.2
keep their value ids. See docs/secrets.md. -
A suppression that covers more than one value is ignored. When a
suppressed location id has seen more than one distinct value in the run, the
secret is treated as unsuppressed, for display and for--fail-on, with the
reason "suppression covers one value; N seen". -
-u user:pwmasking fails closed. The password after-u/--useris
masked for every program except a short list where-unames a user, not a
password (dockerandpodmanexec/run/create,docker compose exec,
sudo,su,ssh,chown,systemctl,gitand similar), and the program
that receives-udecides, past wrappers such assudo,envandtimeout.
A numericuid:gidis never a credential. -
report_sha256no longer moves with the clock. The digest excludes
pricing.age_days,pricing.staleand theAF013coach finding, which change
as days pass over unchanged transcripts.report_sha256values are not
comparable with 0.2.2 reports. This is a known break of digest comparability,
not of schema;schema_versionstays 1.--diffsays so when the digests
differ but nothing it compares has changed. -
Current-generation pricing. Opus 5.5 ($4/$20), Sonnet 5.5 ($2/$10),
Haiku 5.5 ($0.10/$0.50, or $0.50/$2.50 for a message whose prompt exceeds
100,000 tokens), Fable 5.1 and Mythos 5.1 ($10/$50) are vendor rates,
verified 2026-10-08. The cache-read multiplier is now per model: 0.10 by
default, 0.05 for Opus 5.5 and Sonnet 5.5, 0.025 for Fable 5.1 and Mythos 5.1.
It applies to Claude Code, the subagent cost floor, Copilot and the cache
savings figure. An unknown model in a known family is now priced from the
newest known sibling, not the most expensive one. Fast mode, batch and data
residency are not modelled.
Added
--network --htmlwrites the network report as one offline page:
inline SVG charts, sortable tables, every item with its trace, and the
--network --jsonobject embedded with its sha256. It never overwrites
(actualis-network-2.html, …), is readable by you only, and a content
security policy blocks every fetch.--outnow applies to--cardor
--html, and--with-promptsto--jsonor--html.--networkprints only the NETWORK section (with the IOC block when
--iocis given), and--network --jsonemits only thenetworkobject. The
full report gets a one-linenetworksummary in FLEET when downloads exist.
The view reads top down, every line at most 100 columns:- a chart strip: an approval bar (unasked solid, unknown medium, asked
light), downloads per day as a sparkline with the unasked on the same
scale under it (empty days blank), and the mix of kinds; - TOP HOSTS, TOP PACKAGES and, with two or more projects, BY PROJECT: up to
ten rows each, a bar whose solid part is the unasked share, and
unasked / all; - UNASKED, BY SESSION (UNKNOWN when nothing is unasked): the newest five
sessions, each with its first five such downloads in the order they ran,
the agent'swhy, and theactualis --network --session <id>command
that lists them all.
The charts need no colour, and an ASCII-only terminal gets ASCII (#,.,
:and_.:-=+*).--explain networknames the--jsonpath behind each
chart.
- a chart strip: an approval bar (unasked solid, unknown medium, asked
--host,--package,--sessionand--unaskednarrow--network
(text and--json), combinable with each other,--projectand--days.
--hostis a suffix on a label boundary,--packagethe exact name (npm
case-sensitive, pypi by PEP 503),--sessiona prefix. The filter runs
before--network-strictand--ioc, so totals, findings and matches are of
the filtered set. Filtered, the text view prints the chart strip for the
filtered set, then a block per item under a line naming its session, newest
first and capped at--topx 5: host and target, when, agent, project and
session, approval with the mode it was judged from, the redacted command,
why, the prompt, and atraceline (call id, file and line) that
sed -n '<line>p' <file>turns into the exact record. Without--network
each is a usage error.network.by_day[],network.by_day_undated,network.by_project[]and
network.packages[].unasked, the series the charts draw. They reconcile
withnetwork.totals(dated plus undated equals all items; project sums
equal the item and unasked totals), and the text view says so in red when
they do not.network.items[]gainsmode,why,promptandtranscript.mode
is the permission-mode key approval was judged from.whyis the agent's
own text before the call (last sentence, 160 characters);promptis the
person's last real message before the turn (160 characters), emitted only
with the new--with-promptsandnullotherwise. Both are redacted and
cleaned when read.transcriptis{root, file, line}. What Claude Code,
Codex and Copilot each provide is inactualis --explain network.
schema_versionstays 1;--shareand--cardcarry none of it.secrets[].distinct_valuesin--jsonand in the MCPexposed_secrets
tool: how many different values were seen under the id in this run. The
text report says "N distinct values" when it is more than 1.- More unreadable shapes are counted in
unreadable_commands: ANSI-C
quoting that spells a letter ($'\x63url'), a substitution piped to a shell,
and an inline script that reaches the network (python -cwithurllib,
requestsorsocket,node -ewithfetch,perl -MLWP). They are
counted, not flagged. bash.oversized_commandscounts commands over 32 KB, and each raises a
mediumoversized-commandflag (see Fixed).--ioc FILE: did an agent install the bad version? Matches every
download the transcripts show in the network inventory against a known-bad
list, offline and only
when named: theactualis-iocline format (npm:x@=1.2.3||=1.2.4,
pypi:name,host:evil.example) or OSV JSON/JSONL. Names compare as each
ecosystem does (npm and Go exactly, pypi by PEP 503, images through the shared
Docker Hub normaliser); versions by SemVer 2.0.0 or PEP 440, written out in
the standard library. A match is a high finding (network-ioc); a listed name
whose version cannot be decided is medium (network-ioc-unresolved), never
clean. Refused calls are listed and never fail a gate, and the trust list
never exempts a match.network.iocin--json(always an object),
network.items[].ioc, an IOC block in NETWORK,--explain iocand
docs/ioc.md. Any problem loading a list, or a list with
nothing checkable, exits 2; every size, line, entry and nesting limit is hard.
The Action gains aniocinput and anioc-matchesoutput.- Network inventory. A NETWORK section and a
networkkey in--jsonlist
every download and fetch the transcripts show and actualis can read —
packages installed, repos cloned, URLs fetched, including those behind
control flow, wrappers (sudo,timeout,xargs,eval),bash -cand
substitutions — and whether a person approved each (asked,unasked, or
unknownin default mode, where an allowlist rule may have allowed it
silently).--network-strictturns unasked downloads from sources not in
--network-trust/.actualis-network-trustinto medium findings. The
Action gainsnetwork-strictandnetwork-trustinputs.schema_version
stays 1. - Audit-config finding. A high-severity finding that cannot be suppressed
fires when an agent writes.actualis-network-trust,.actualis-suppressions
or the user-level~/.config/actualis/suppressions, or runs
actualis --suppress. It is a heuristic tripwire, not a prevention: it reads
the commands and file-write tools the transcript shows (case-insensitively),
on the dequoted text, through assignments,cd, collapsed paths, globs and
the same wrappers the inventory reads. Any command that names a config file
without being a known reader (cat,grep,git diffand the like), and a
reader that redirects or writes into one, trips it. It misses Codex
apply_patchedits, writes made by a script run from another file, and
anything built at run time.actualis --suppressrefuses its id, and
--suppressionsmarks a suppression of it as ignored. It can newly fail
--fail-on high. - Trust provenance. The trust sources in force (
--network-trust, or the
file with its path and sha256) appear in the report and as
network.trust_sourcesin--json. --explain networkexplains the network inventory.
Fixed
-
Newer Codex sessions no longer stop the scan. Codex 0.16x writes
approval_policyas an object ({"granular": {...}}). actualis records
it ascodex:granular, which does not count as unattended, so downloads
under it show as unknown. 0.2.2 showed the raw object as the mode name.
Any non-text value in a transcript field is now read as empty. -
Shell, secret and network counts no longer double-count tool calls copied
into resumed or forked sessions. The sametool_useid in more than one
transcript is counted once (duplicate_tool_calls_skippedin--json), so
counts can drop slightly against 0.2.2 (about 0.5% on one real corpus). -
Security:
redact()missed more shapes of embedded token. It now masks
URL userinfo that has no password (https://TOKEN@host), all of the
userinfo up to the last@, and scp-style userinfo containing:or longer
than 20 characters. Before this, such tokens could appear in shell-audit
evidence and in--json. -
Security: control characters in transcripts are stripped from network
items, so a crafted command cannot inject terminal escapes into the report. -
bash.flags[].had_secretis declared in the JSON schema. It was always
emitted. -
Counts rise against 0.2.2 baselines.
secret_exposuresand
secret_projectsnow count usernames in URL userinfo (postgres://admin@db),
credentials passed as option values (curl -u user:pw,wget --password,
docker login -p),PASSWORD/PASSWD/PASSPHRASEvariables of 6 to 11
characters, URL passwords under 6 characters, scp-style passwords,
password-less URL or scp userinfo over 20 characters, andAuthorization:
header values with no recognised prefix. These arehigh, so they can
newly fail--fail-on high.unreadable_commandsrises with the new shapes
(see Added). Compare against a fresh baseline, not a saved one. -
Security: credentials passed as option values are masked in evidence,
the text report and--json(curl -u user:PASSkeeps the user readable). -
Security: terminal escapes in audit-config evidence and harness fields
(permission mode, denial kind, model, approval and sandbox policy) are
stripped at ingest. -
Security: redaction runs before a cut. Flag evidence and oversized
command text are redacted, then cut to their caps, so a token split by the
cut cannot survive. -
pinnedmeans an exact version.npm i x@4,cargo add x@1.2and
go install m@v1.2are ranges or prefixes, not pins. Image names are
normalised (docker.io/library/nginxisnginx),npm i github:o/rcarries
a URL, an npm alias records its target (aliasfield), andcargo addand
cargo installare told apart inprogram. -
Strict mode no longer flags a remote name (
git pull origin). A remote
added or cloned earlier in the same session resolves to its host and is judged
normally. -
A command over 32 KB is a signal, and padding no longer hides what follows
it. Each is counted once inbash.oversized_commands(and as unreadable),
shown as "N commands over 32 KB were only partly audited", and raises a
mediumoversized-commandflag. The audit-config tripwire, the remote-exec
shape and credential detection now read the whole command in overlapping
32 KB windows, up to 1 MiB; past that the command is counted unreadable.
Redacted text cut at the scan cap ends in…[truncated]. -
-u user:pwno longer masks a uid:gid (docker exec -u root:wheel).