The last development note was on May 17. I had just come back from being sick for a few days, and the work then was the first practical bridge from the Python registry to the Rust registry: live deployment shape, operator-token parity, and migration tooling. The week since then has been the same kind of work, just deeper in the stack. Less visible, more important.
Oversight is a security protocol. That means the release story cannot only be about the happy path: seal a file, open a file, verify a bundle. The ugly cases matter more. What happens when a migrated database has an orphaned beacon row? What happens when an event points at a transparency log index that exists, but the leaf at that index is for different evidence? What happens when the local tlog file has a corrupted line? What happens if a mobile release quietly grows a network permission or a telemetry dependency?
This week's answer is that those cases now fail louder.
Registry burn-in got stricter
The Rust Axum registry has moved from "can serve the v1 surface" toward
"can survive operator migration without hiding bad evidence state." The
new oversight-registry --validate-db path started by checking
the database relationships that matter after a Python-to-Rust import:
manifests, beacons, watermarks, events, corpus rows, identity bindings,
manifest signatures, and manifest/file ID agreement.
That validator now goes further. It rejects malformed event
extra JSON and malformed corpus metadata JSON. It reports
duplicate transparency-log indexes, negative indexes, missing event tlog
indexes, and indexes outside the on-disk tlog size. Then it checks the
part that is easy to miss: an event row's tlog index must point at a
leaf whose payload actually matches that event row.
An in-range index is not enough. If row 24 says a DNS beacon fired for token A, but tlog leaf 24 is a different beacon for token B, the database is not clean. It may be accidental corruption, a failed migration, or a bad operator action, but it is not evidence an auditor should accept. The Rust validator now compares the event kind, token, file binding, recipient binding, source IP, user agent where applicable, timestamp, and DNS sidecar fields against the indexed tlog leaf.
That is boring. It is also exactly the kind of boring a registry needs before it becomes the default backend.
The tlog itself now fails closed on recovery
Yesterday's validator work caught database rows pointing at unrelated
leaves. Today's follow-up tightened the local transparency log itself.
The Rust oversight-tlog crate used to recover existing
leaves permissively: malformed lines or bad hashes could be skipped
during startup. That is the wrong posture for an append-only evidence
log. If the log on disk is corrupted, the service should say so.
Recovery now rejects malformed leaf records, non-contiguous indexes,
bad hash lengths, and leaf-hash mismatches. New leaf records also carry
leaf_data_hex, which preserves the exact bytes used to
compute SHA-256(0x00 || leaf_bytes). The older
leaf_data field remains as the readable JSON/display value
for registry events, so the public range endpoint stays usable for
humans and existing consumers.
This is a small schema hardening, but it closes a real class of
ambiguity. A verifier or monitor can recompute the RFC 6962 leaf hash
from exact bytes instead of trusting a lossy UTF-8 display field. The
registry v1 spec now says that explicitly for /tlog/range.
Writes fail closed too
The registry write paths were tightened from the other side as well. Register, HTTP beacon, DNS beacon, OCSP-style beacon, and license-style beacon writes now fail if the local transparency log cannot append. Before that change, a service could store a new database row while failing to write the audit-trail leaf. That is a split-brain evidence problem. It is better to return an internal error than to create a row that looks real but cannot be proven against the local log.
These three pieces line up: append must succeed before evidence is stored, database rows must point at real matching leaves, and recovered leaves must verify against their own hashes. That is the shape I want before the Rust registry is called stable.
Format parity kept moving
The registry was the main thread, but it was not the only one. The Rust format adapters also picked up two parity improvements after the last post.
PDF fingerprint text now uses lopdf page extraction plus a
parsed content-stream fallback, instead of raw literal scanning. Tests
cover Tj, TJ, quote operators, array spacing,
and page-level extraction from a generated fixture. Image watermarking
now ports the Python adapter's DCT mid-band spread-spectrum path using
rustdct, while preserving the blind LSB recovery path for
candidate extraction.
Both changes matter because Oversight's Rust path is not meant to be a second, approximate implementation. The goal is one protocol surface with Python as the original reference and Rust as the operational core. Format adapters cannot drift quietly if the registry and mobile app are going to rely on the Rust crates.
The mobile verifier got privacy guardrails
The mobile repo also changed in a way that is deliberately unglamorous. Recent verification history is session-only now, and the app clears the old persisted history key on boot and after verification. Issuer IDs, filenames, and content-hash summaries should not sit in mobile backups just because the user verified a bundle once.
Android and iOS CI now upload an
oversight-mobile-build-manifest.json alongside artifacts.
The manifest records commit, ref, lockfile hashes, toolchain settings,
artifact paths, sizes, and SHA-256 hashes. That is not full
reproducible builds yet, but it is the bridge artifact until byte-for-
byte verification lands.
The newest guard is scripts/privacy_guard.py. Both mobile
workflows run it. It fails if release manifests request network,
location, microphone, or contacts access, or if telemetry and crash SDKs
are added to pubspec.yaml. The mobile verifier's privacy
claim is simple: no accounts, no telemetry, no server-side verification
requirement. CI now protects that claim from accidental drift.
Where this leaves main
The current tagged release remains v0.4.11, the hardware-key
completion line across Python, Rust, and the browser inspector. The work
described here lives on main after that tag. It is pointed at
the next gate: Rust registry burn-in, then a wire-format stability
statement, then a v1.0 declaration when the implementation has earned
it.
This week did not add a flashy feature. It made bad states harder to ignore. That is the progress I care about right now. A security protocol is not bulletproof because its diagram looks good. It gets closer by refusing to lie when storage, migration, logs, and release pipelines go sideways.
Relevant public references: docs/REGISTRY_DEPLOYMENT.md, docs/spec/registry-v1.md, docs/ROADMAP.md, and the mobile verifier repo at oversight-protocol/oversight-mobile.