L5: dependency-audit.yml now generates a CycloneDX SBOM from
requirements.lock and uploads it. pip-audit answers "are there known CVEs
today"; an SBOM answers "which exact versions were in this build", which is
the question that arises when something is disclosed later against a release
already in the wild.
Verified against the real tool rather than written from memory -- the first
flag spelling (--output-format / --outfile) does not exist in cyclonedx-py
and would have failed in CI. The working form is
`--of json --output-reproducible -o <file>`, which emits CycloneDX 1.6 with
96 components and records [email protected], [email protected],
[email protected], [email protected], [email protected], [email protected] and
[email protected].0 at exact versions.
L4: torch/services/memory/index.js built its temp filename from Date.now()
plus Math.random() before an atomic rename -- predictable enough to
pre-create or symlink between the write and the rename. Now randomBytes(8).
Also renames the Math.random-derived value in relay-health.mjs from `nonce`
to `probeSuffix` and draws it from randomBytes(6). It is a public probe tag
used to correlate a publish with its readback, not a cryptographic nonce,
and nothing was wrong with it -- but the name invited someone to copy the
pattern somewhere it would matter.
torch has no test/ directory in the repo, so there is no suite to run for
these two files; both were syntax-checked and confirmed to import cleanly.
The SeedPass determinism gate is green (84 tests).
Not done, and recorded as a decision in TODO.md instead: torch/_backups/
holds 5240 tracked files across six dated snapshots. Untracking them is a
large deletion and not something to do as a side effect of an entropy audit.
They currently duplicate every security-relevant grep hit, and bare
`node --test` in torch/ discovers them and fails 18 tests from stale
snapshot code.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01HtNMsKzzFTPURemX7tT19u
Passwords are never stored -- they are re-derived on demand from
(seed, index, length, policy) -- and entries carried no algorithm version.
Any change to generate_password therefore changed every existing password in
every vault, unrecoverably. This adds the version field that makes such
changes possible at all, then makes three.
Safety net first (M4). test_entropy_integrity.py freezes 18 v1 vectors
spanning every policy and both sides of the 32-byte stream-wrap boundary,
and asserts the RNG fails closed: os.urandom raising must abort seed
generation, short reads must abort, AES-GCM nonces must be fresh. Each guard
was mutation-verified rather than assumed -- removing _balance_distribution,
perturbing the stream wrap, or altering the character mapping each turns all
18 vectors red, and adding a hash-of-timestamp fallback to
generate_bip85_seed turns the fail-closed test red.
generate_password now takes gen_version, defaulting to 1. Existing entries
carry no field and keep deriving v1 byte-identically; the argument is only
forwarded when non-legacy, so all 13 call sites across CLI, API and both
TUIs are untouched. New password entries are stamped gen_version 2. An
unreadable version raises rather than guessing which algorithm produced a
password.
v2 fixes three findings:
- M1: drops the forced equal-quarters class quota. 1 -> 82 distinct class
compositions at length 16 over 200 samples; per-character entropy
6.42 -> 6.53 bits against a 6.555 ceiling.
- M2: rejection sampling instead of byte % len(alphabet). Chi-square 96.1
against df=93 (p<0.001 critical value ~147).
- M3: an unbounded HMAC-expanded stream instead of cycling a 32-byte key,
which was a repeating pad for passwords over 32 characters. Lag-32 repeat
rate now sits within one standard error of chance.
Dropping the quota removed an accidental backstop: v1's minimum enforcement
overwrites uniformly random positions, so raising one class minimum could
destroy another, and only the quota's near-total rewrite hid it. v2 chooses
donor positions solely from classes that currently have a surplus, and
rejects a policy whose minima cannot fit the requested length.
Also removes a dead HKDF derivation whose result was discarded while the
docstrings named it as the algorithm in use (L1); PBKDF2 was and remains the
real one. Verified as a negative control -- the vectors stayed green.
Six test doubles are updated to the real generate_password signature; they
began receiving gen_version once new entries carried it.
Existing entries stay on v1 by design. An opt-in per-entry upgrade, with a
confirmation making the consequence unmissable, is still to come.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01HtNMsKzzFTPURemX7tT19u
Audits every security-sensitive random value in src/ against a
Coldcard-class checklist: where each bit of unpredictability originates,
whether that source is present in the production artifact, and what happens
when it fails.
No predictable-fallback defect. Master seed, AES-GCM nonces, TOTP secrets,
Argon2 salts, API tokens and agent leases all come from os.urandom or
secrets and fail closed. The random module is not imported anywhere in
non-test source.
The findings are about how much entropy survives the deterministic
derivation in core/password_generation.py, not about whether it should be
deterministic. Chief among them: _balance_distribution forces exactly L/4 of
each character class into every password, verified 300/300 at five lengths.
That costs 5.7-11.5 bits and, more materially, fingerprints the generator --
a password with exactly a quarter of each class is a strong signal it came
from SeedPass.
Records the compatibility constraint that governs all of it: passwords are
never stored, they are re-derived on demand, and entries carried no
algorithm version. Any in-place change to generate_password was therefore a
data-loss event, not an improvement.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01HtNMsKzzFTPURemX7tT19u
- SearchService.graph_pivot_results(): wraps linked_neighbors() output
in SearchResult model with match_reasons (graph_pivot, relation:X,
direction:X, atlas:scope), scores (hop-distance based), and relation
meta string for UI surfaces
- AtlasWayfinderScreen: propagates atlas_source_scope to LinkedItemsPanel
when opening entries or recent items from the wayfinder
- LinkedItemsPanel: adds #linked-atlas-context label (◈ Atlas context:
scope_path) that refreshes via property setter; atlas scope is
preserved across linked-panel pivots; _update_atlas_context_label()
helper keeps display in sync
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Add PivotBreadcrumbBar widget to inspector.py showing multi-hop path
- Add _pivot_history/pivot_to_entry/action_pivot_back to app.py
- LinkedItemsPanel: group linked items by relation type with section headers
- LinkedItemsPanel: b key for back navigation, breadcrumb rendered from app history
- External entry selection (grid) clears pivot history; pivot chain preserved on linked nav
Add _stdin_is_tty() helper and check at start of _launch_tui3_or_exit.
When stdin is not a TTY, emit a clear error message and exit 1 instead
of propagating an EOF exception from seed setup prompts.
Add test for non-interactive stdin error path; patch _stdin_is_tty in
existing tests that expect tui3 launch to proceed.
Fixes SeedPass-6d26
- maintenance.py: add status icons to format_status, add warning CSS class
- profile.py: improve intro text, use ► marker for active profile
- pubkey.py: use explicit Binding() for keybindings
- relays.py: remove stray `import traceback` in _perform_sync
- security.py: clarify backup passphrase label
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Remove 'qr' from managed_account context actions (accounts don't expose
a QR-scannable key like seeds/nostr do)
- Change bare 'except:' to 'except Exception:' on vault manager check
- Add test_v3_action_bar_managed_account_excludes_qr in parity tests
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Settings screen now renders a UTILITY & MAINTENANCE card listing stats,
db-export, db-import, checksum-verify/update, totp-export, and offline
mode commands. Renders gracefully when config service is offline (no
early return — all sections including utility always display).
Inspector adds UtilityHintsBar widget that persists across board swaps
via a #board-slot Vertical. Hints update to show kind-appropriate
palette commands (copy, reveal keybinds, QR, npub, managed-load, etc.).
BoardContainer now routes managed_account to its own SeedBoard hint set.
Two new smoke tests verify the utility section renders and that hints
update correctly for password, nostr, and idle states.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Update agent models from gpt-5.3-codex to claude-compatible names
(opus for coordinator/lead, sonnet for builder/scout/reviewer/merger,
haiku for monitor)
- Switch runtime from codex to claude (codex requires interactive TTY,
incompatible with tmux-based agent spawning)
- Add Overstory ecosystem links to AGENTS.md and CLAUDE.md
- Add docs/overstory_quick_reference.md with startup, monitoring, and
CLI commands
- Sync seeds issue state
Co-Authored-By: Claude Opus 4.6 <[email protected]>