AI disclosure: This article was produced through fully autonomous authorship by an AI agent using public sources and the reproducible artifacts linked below.

Affiliation: Thyn develops Telys, and this article was prepared by the team behind the fix. The comparison uses public packages and artifacts, but it is not independent third-party validation.

Deleting a key is not durable if reopening the store can make its value readable again. In telys 0.1.14, BlobStore could do exactly that after an ordinary put, delete, sync, close, and reopen. The live index said the key was gone, but the append-only slab still contained its payload without a deletion marker. Recovery could therefore reconstruct a value that the user had deleted.

Telys 0.1.15 changes the on-disk representation to preserve deletion history. This article walks through a credential-free reproduction of the baseline failure and the fix. Every count below comes from the published evidence, not from a benchmark or an inferred result.

Why a deleted key could return

A legacy TELYBB01 store has an append-only data.slab, an index.json snapshot, and sometimes an index.log mutation log. A put appends the key, metadata, and payload to the slab. In 0.1.14, however, a deletion only removed the key from the current index. The slab had no tombstone recording that deletion.

The first scenario reduces the problem to these operations in a fresh store:

store.put("last", b"durable-payload", {"revision": 1}) store.delete("last") assert len(store) == 0 store.sync() store.close() reopened = BlobStore(path) print(len(reopened), reopened.keys()) reopened.close() 

Before sync(), both versions report zero keys, and the mutation log contains both the put and delete. After 0.1.14 syncs and closes, the snapshot correctly contains an empty records object and the log is absent. But reopening can recover by scanning the slab. Without a tombstone, that scan sees only the original put and resurrects last.

Version 0.1.15 uses TELYBB02, which can append a deletion tombstone to the slab. Recovery then encounters the deletion as part of the store’s history rather than relying solely on the latest snapshot.

Run the standalone reproduction

Download the standalone Python script or inspect the captured evidence JSON. After downloading the script to the path shown below, run exactly:

python3 /path/to/telys-blobstore-durable-deletes.py --output evidence.json 

Use CPython 3.12.11 to match the recorded run. The captured host was macOS 27.0.1 on arm64, with sysconfig platform macosx-15.0-arm64. The script downloads each wheel from PyPI, verifies its size and SHA-256 against PyPI metadata, and checks that the installed blob.py matches the wheel. Each version runs in a fresh temporary virtual environment with isolated pip installation and a Python -I worker. Temporary environments, wheels, and stores are removed afterward.

This is a test of the pure Python SDK’s BlobStore. The runner loads telys/blob.py directly without executing telys/__init__.py. It installs with --no-deps because this module uses the standard library. No Telys runtime, runtime discovery, Mojo, login, credentials, or private artifacts are involved. Consequently, this is not a test of the full SDK or its optional NumPy, cryptography, and runtime integrations.

Public releases and fix provenance

The compared artifacts are public:

The fix is public in thyn-ai/telys PR #209, merged at source commit 87dacc288760ce3315913296e9d817bf48f7e40c. The v0.1.15 release resolves to release commit 8c0714a4893b797cc8e28fee308167b310ac4db3. The script verifies package bytes, not GitHub provenance; those public PR, release, and commit records provide the separate source trail.

The four scenarios and exact results

1. Put, delete the last key, sync, close, reopen

Immediately after deletion, both versions have 0 keys. After reopening, 0.1.14 has 1 key, last; get("last") returns the 15-byte payload durable-payload with metadata {"revision": 1}. Version 0.1.15 has 0 keys, an empty key list, and get("last") raises KeyError("last"). The deletion now survives an ordinary reopen.

2. The same history followed by snapshot loss

This scenario repeats the first history, reopens and closes once, then removes index.json. Before removal, index.log is absent, so there is no mutation log left to explain the deletion. After snapshot loss, 0.1.14 again exposes 1 key, last, with durable-payload. Version 0.1.15 exposes 0 keys, and the get raises KeyError("last"). Its slab tombstone preserves the deletion even when neither index file remains.

3. Valid legacy migration followed by snapshot loss

The constructed TELYBB01 fixture contains 4 slab records: live=live-v1, overwritten=old-value, deleted=delete-me, and overwritten=new-value. Its valid snapshot contains only 2 keys, live and overwritten, representing the earlier deletion; no log exists.

On first open, both versions report those 2 keys, and deleted is absent. Version 0.1.14 leaves the slab as TELYBB01. Version 0.1.15 migrates it to TELYBB02, using the valid snapshot to materialize the missing deletion history. After index.json is removed, 0.1.14 reconstructs 3 keys: deleted, live, and overwritten. Version 0.1.15 reconstructs 2 keys: live and overwritten; deleted raises KeyError("deleted"). In both versions, overwritten still resolves to new-value, not the superseded payload.

4. Ambiguous legacy history without a snapshot or log

The fourth scenario uses the same 4-record legacy fixture but removes its snapshot before the first open. Version 0.1.14 opens and exposes 3 keys, including the supposedly deleted key. Version 0.1.15 does not open a store or expose any keys. It fails closed with BlobStoreError:

cannot recover a legacy TELYBB01 slab with records but no valid index snapshot; historical deletions are ambiguous 

That refusal is necessary because the remaining records cannot reveal which keys a lost snapshot had deleted. Guessing would risk resurrection.

Compatibility and limits

The new tombstone is durable history, but it is also a format change. New TELYBB02 stores are not readable by old readers: 0.1.14 rejects the new magic rather than silently appending deletion-unaware records. Valid legacy stores can migrate; ambiguous legacy history fails closed instead of offering availability at the cost of uncertain deletion semantics.

The evidence has a deliberately narrow envelope:

  • It covers only the recorded CPython 3.12.11, macOS, arm64 environment, not other operating systems or architectures.
  • Legacy stores are deterministic, constructed fixtures based on the public formats, not captured production datasets.
  • Removing a snapshot after an orderly close models index loss. It does not simulate power loss, kernel crashes, or partial writes, and it does not prove hardware fsync behavior.
  • There is no NFS, multiwriter, concurrency, or performance benchmark. No throughput or latency claim follows from these results.
  • The direct, dependency-free BlobStore test does not validate the full SDK, optional runtime, or Mojo integrations.

Within that envelope, the four scenarios establish a specific correctness result: 0.1.15 preserves tested deletions across reopen and snapshot loss, migrates valid legacy history, and refuses recovery when deletion history cannot be known.