Valkey Datastore
Stores swamp runtime data in Valkey/Redis with sorted-set path indexing for O(log n + k) prefix lookups and SET NX distributed locking. Compatible with local Valkey, AWS ElastiCache Serverless, and AWS MemoryDB. Implements two-phase sync to minimize time under the global lock. Emits OpenTelemetry spans for Valkey commands, lock acquisition/release, and push/pull sync so datastore activity is visible in traces when a TracerProvider is configured.
2026.08.28.2
Changed: Normalized the extension license to Apache-2.0 and corrected the copyright holder to "Sean Escriva". Extensions that previously shipped an MIT LICENSE.md are now Apache-2.0, consistent with the repository root and every other extension. No code or behavioral changes.
Upgrade note: License text only. No API, schema, or runtime behavior changed.
2026.08.28.1
Changed: Test suite is now fully hermetic — the lock, sync, and verifier tests run against an in-memory fake and no longer require a live Valkey server or a CI service container. The extension rejoins the standard CI test matrix.
Added: createValkeyLock, createSyncService, and createValkeyVerifier
are now exported from the module. They were already the internal building
blocks of createProvider; exporting them lets callers (and tests) inject a
client. Behavior of the datastore provider is unchanged.
2026.08.26.3
Fixed: Restored inline npm:zod@4.4.3 import specifiers so the registry
quality scorer can resolve dependencies and score the extension. An earlier
release used a bare "zod" import-map specifier, which published but scored as
unscored.
Changed: Retained explicit compilerOptions.strict in deno.json. No
behavioral or schema changes.
Stores swamp runtime data in Valkey/Redis with sorted-set path indexing
Config Fields
| Field | Type | Description |
|---|---|---|
| url | string | Valkey/Redis connection URL (redis:// or rediss:// for TLS) |
| prefix | string | Key namespace prefix for all swamp data |
| db | number | Redis database number |
| password? | string | Auth password (prefer vault expression for production use) |
| connectTimeoutMs | number | Connection timeout in milliseconds |
| maxRetriesPerRequest | number | Max retries per command before failing |
2026.08.28.1
Changed: Test suite is now fully hermetic — the lock, sync, and verifier tests run against an in-memory fake and no longer require a live Valkey server or a CI service container. The extension rejoins the standard CI test matrix.
Added: createValkeyLock, createSyncService, and createValkeyVerifier
are now exported from the module. They were already the internal building
blocks of createProvider; exporting them lets callers (and tests) inject a
client. Behavior of the datastore provider is unchanged.
2026.08.26.3
Fixed: Restored inline npm:zod@4.4.3 import specifiers so the registry
quality scorer can resolve dependencies and score the extension. An earlier
release used a bare "zod" import-map specifier, which published but scored as
unscored.
Changed: Retained explicit compilerOptions.strict in deno.json. No
behavioral or schema changes.
2026.08.26.3
Fixed: Restored inline npm:zod@4.4.3 import specifiers so the registry
quality scorer can resolve dependencies and score the extension. An earlier
release used a bare "zod" import-map specifier, which published but scored as
unscored.
Changed: Retained explicit compilerOptions.strict in deno.json. No
behavioral or schema changes.
2026.08.26.1
Changed: Normalized deno.json configuration for repo-wide consistency:
added explicit compilerOptions.strict and migrated zod dependency to the
import map (bare "zod" specifier instead of inline npm:zod@4.4.3). No
behavioral changes — runtime resolution is identical.
2026.08.25.1
Changed: Updated labels for improved extension discoverability. Added cross-cutting category labels (security, observability, finops, infrastructure, networking, compliance, devops, ai, incident-response) where applicable.
updated labels
2026.08.24.1
Added Troubleshooting section documenting silent heartbeat/release failures, PATH_LIMIT cap, dirty paths overflow, network error non-retry, TLS configuration, tombstone GC behavior, and single-connection architecture.
2026.08.21.2
Changed: Valkey command failures now name the operation and key they were
acting on instead of surfacing the bare driver error (e.g. a NOSCRIPT reply
now reads as Valkey EVAL on key "swamp:_lock:..." failed: NOSCRIPT). The
original error's name is preserved, so anything matching on
error.name/instanceof is unaffected. Tombstone garbage collection during
pushChanged/commitPush is now wrapped so a connection blip during GC can no
longer fail an otherwise-successful push — previously an unguarded pipeline
failure there propagated and discarded a push that had already committed its
writes.
2026.08.21.1
Changed: Added a description to the previously-undocumented
tls.rejectUnauthorized field in the config schema, explaining what it verifies
and when to disable it.
2026.08.02.1
Changed: Bump ioredis 5.11.1 → 6.0.0
2026.07.29.1
Added: Incremental pull via sorted-set scores. Pull now fetches only paths
changed since lastPulledSeq using ZRANGEBYSCORE, converting pull from
O(total files) to O(changed files).
Added: Deleted paths are retained in the sorted set with deleted: "true"
metadata so incremental pulls receive tombstone signals. Previously, ZREM on
delete made deletions invisible to score-range queries.
Changed: Dirty-path diffs are collected in parallel and applied in a single
applyChanges call (1-3 pipeline flushes instead of N separate cycles).
Changed: INCR for the commit sequence now runs before pipeline writes, not
after. Each concurrent writer atomically reserves a unique seq. A wasted seq on
crash is acceptable — seq gaps do not affect correctness.
Changed: Pull throws on PATH_LIMIT truncation instead of silently dropping paths beyond 50,000.
Fixed: ZSCAN MATCH patterns now escape glob metacharacters (*, ?, [,
], \) preventing mismatches on paths containing those characters.
Fixed: pathsForPrefixes deduplicates results when scope prefixes overlap.
2026.07.27.1
Changed: Bump @opentelemetry/api 1.9.0 → 1.9.1
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. Valkey round
trips emit one span each (Valkey SET, Valkey ZRANGEBYLEX,
Valkey GETBUFFER, Valkey EVAL, …) carrying db.system.name,
db.operation.name, and the key. Pipeline flushes emit
Valkey pipeline writeFiles / deleteFiles / fetchMetadata / fetchHashes
with the batched command count. The lock emits valkey-datastore lock acquire /
release / withLock / inspect / forceRelease, with acquire recording wait
duration and whether it contended. The sync service emits
valkey-datastore pullChanged / pushChanged / hydrateFile / preparePush /
commitPush with file counts, path counts, the remote sequence number, and
fast-path indicators.
Added: Lock contention retries are recorded as retry span events on the
acquire span.
Added: Pipeline spans report valkey.pipeline.failed_commands and are
marked as errors when any command in the batch failed. pipeline.exec()
resolves with per-command errors rather than rejecting, so a partially failed
batch would otherwise have left a span reporting success. pullChanged also
reports datastore.files_skipped for paths whose metadata read failed, which
were previously dropped silently.
Changed: pushChanged and commitPush now report datastore.files_pushed
and datastore.files_deleted separately. applyChanges returns a single count
covering writes and deletes together, and reporting that as a file count would
have overstated pushes whenever tombstones were involved. The value returned to
callers is unchanged.
Changed: Nothing else observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the TracerProvider,
and every span is a no-op when none is registered.
Note: Spans are placed on the round trips that carry latency — index range
scans, blob reads, and pipeline flushes — rather than on every one of the
seventeen command call sites. Heartbeat renewals are deliberately uninstrumented
so a long-held lock does not bury the trace in periodic PEXPIRE spans.
Note on secrets: Blob values are file content and the connection URL embeds a password. Neither is ever recorded; span attributes carry only command names, key names, and counts.
2026.08.21.1
Changed: Added a description to the previously-undocumented tls.rejectUnauthorized field in the config schema, explaining what it verifies and when to disable it.
2026.08.02.1
Changed: Bump ioredis 5.11.1 → 6.0.0
2026.07.29.1
Added: Incremental pull via sorted-set scores. Pull now fetches only paths
changed since lastPulledSeq using ZRANGEBYSCORE, converting pull from
O(total files) to O(changed files).
Added: Deleted paths are retained in the sorted set with deleted: "true"
metadata so incremental pulls receive tombstone signals. Previously, ZREM on
delete made deletions invisible to score-range queries.
Changed: Dirty-path diffs are collected in parallel and applied in a single
applyChanges call (1-3 pipeline flushes instead of N separate cycles).
Changed: INCR for the commit sequence now runs before pipeline writes,
not after. Each concurrent writer atomically reserves a unique seq. A wasted seq
on crash is acceptable — seq gaps do not affect correctness.
Changed: Pull throws on PATH_LIMIT truncation instead of silently dropping paths beyond 50,000.
Fixed: ZSCAN MATCH patterns now escape glob metacharacters (*, ?, [,
], \) preventing mismatches on paths containing those characters.
Fixed: pathsForPrefixes deduplicates results when scope prefixes overlap.
2026.07.27.1
Changed: Bump @opentelemetry/api 1.9.0 → 1.9.1
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. Valkey round
trips emit one span each (Valkey SET, Valkey ZRANGEBYLEX,
Valkey GETBUFFER, Valkey EVAL, …) carrying db.system.name,
db.operation.name, and the key. Pipeline flushes emit
Valkey pipeline writeFiles / deleteFiles / fetchMetadata / fetchHashes
with the batched command count. The lock emits
valkey-datastore lock acquire / release / withLock / inspect /
forceRelease, with acquire recording wait duration and whether it contended.
The sync service emits valkey-datastore pullChanged / pushChanged /
hydrateFile / preparePush / commitPush with file counts, path counts, the
remote sequence number, and fast-path indicators.
Added: Lock contention retries are recorded as retry span events on the
acquire span.
Added: Pipeline spans report valkey.pipeline.failed_commands and are
marked as errors when any command in the batch failed. pipeline.exec()
resolves with per-command errors rather than rejecting, so a partially failed
batch would otherwise have left a span reporting success. pullChanged also
reports datastore.files_skipped for paths whose metadata read failed, which
were previously dropped silently.
Changed: pushChanged and commitPush now report
datastore.files_pushed and datastore.files_deleted separately. applyChanges
returns a single count covering writes and deletes together, and reporting that
as a file count would have overstated pushes whenever tombstones were involved.
The value returned to callers is unchanged.
Changed: Nothing else observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the
TracerProvider, and every span is a no-op when none is registered.
Note: Spans are placed on the round trips that carry latency — index range
scans, blob reads, and pipeline flushes — rather than on every one of the
seventeen command call sites. Heartbeat renewals are deliberately uninstrumented
so a long-held lock does not bury the trace in periodic PEXPIRE spans.
Note on secrets: Blob values are file content and the connection URL embeds a password. Neither is ever recorded; span attributes carry only command names, key names, and counts.
2026.08.02.1
Changed: Bump ioredis 5.11.1 → 6.0.0
2026.07.29.1
Added: Incremental pull via sorted-set scores. Pull now fetches only paths
changed since lastPulledSeq using ZRANGEBYSCORE, converting pull from
O(total files) to O(changed files).
Added: Deleted paths are retained in the sorted set with deleted: "true"
metadata so incremental pulls receive tombstone signals. Previously, ZREM on
delete made deletions invisible to score-range queries.
Changed: Dirty-path diffs are collected in parallel and applied in a single
applyChanges call (1-3 pipeline flushes instead of N separate cycles).
Changed: INCR for the commit sequence now runs before pipeline writes,
not after. Each concurrent writer atomically reserves a unique seq. A wasted seq
on crash is acceptable — seq gaps do not affect correctness.
Changed: Pull throws on PATH_LIMIT truncation instead of silently dropping paths beyond 50,000.
Fixed: ZSCAN MATCH patterns now escape glob metacharacters (*, ?, [,
], \) preventing mismatches on paths containing those characters.
Fixed: pathsForPrefixes deduplicates results when scope prefixes overlap.
2026.07.27.1
Changed: Bump @opentelemetry/api 1.9.0 → 1.9.1
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. Valkey round
trips emit one span each (Valkey SET, Valkey ZRANGEBYLEX,
Valkey GETBUFFER, Valkey EVAL, …) carrying db.system.name,
db.operation.name, and the key. Pipeline flushes emit
Valkey pipeline writeFiles / deleteFiles / fetchMetadata / fetchHashes
with the batched command count. The lock emits
valkey-datastore lock acquire / release / withLock / inspect /
forceRelease, with acquire recording wait duration and whether it contended.
The sync service emits valkey-datastore pullChanged / pushChanged /
hydrateFile / preparePush / commitPush with file counts, path counts, the
remote sequence number, and fast-path indicators.
Added: Lock contention retries are recorded as retry span events on the
acquire span.
Added: Pipeline spans report valkey.pipeline.failed_commands and are
marked as errors when any command in the batch failed. pipeline.exec()
resolves with per-command errors rather than rejecting, so a partially failed
batch would otherwise have left a span reporting success. pullChanged also
reports datastore.files_skipped for paths whose metadata read failed, which
were previously dropped silently.
Changed: pushChanged and commitPush now report
datastore.files_pushed and datastore.files_deleted separately. applyChanges
returns a single count covering writes and deletes together, and reporting that
as a file count would have overstated pushes whenever tombstones were involved.
The value returned to callers is unchanged.
Changed: Nothing else observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the
TracerProvider, and every span is a no-op when none is registered.
Note: Spans are placed on the round trips that carry latency — index range
scans, blob reads, and pipeline flushes — rather than on every one of the
seventeen command call sites. Heartbeat renewals are deliberately uninstrumented
so a long-held lock does not bury the trace in periodic PEXPIRE spans.
Note on secrets: Blob values are file content and the connection URL embeds a password. Neither is ever recorded; span attributes carry only command names, key names, and counts.
2026.07.29.1
Added: Incremental pull via sorted-set scores. Pull now fetches only paths
changed since lastPulledSeq using ZRANGEBYSCORE, converting pull from
O(total files) to O(changed files).
Added: Deleted paths are retained in the sorted set with deleted: "true"
metadata so incremental pulls receive tombstone signals. Previously, ZREM on
delete made deletions invisible to score-range queries.
Changed: Dirty-path diffs are collected in parallel and applied in a single
applyChanges call (1-3 pipeline flushes instead of N separate cycles).
Changed: INCR for the commit sequence now runs before pipeline writes,
not after. Each concurrent writer atomically reserves a unique seq. A wasted seq
on crash is acceptable — seq gaps do not affect correctness.
Changed: Pull throws on PATH_LIMIT truncation instead of silently dropping paths beyond 50,000.
Fixed: ZSCAN MATCH patterns now escape glob metacharacters (*, ?, [,
], \) preventing mismatches on paths containing those characters.
Fixed: pathsForPrefixes deduplicates results when scope prefixes overlap.
2026.07.27.1
Changed: Bump @opentelemetry/api 1.9.0 → 1.9.1
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. Valkey round
trips emit one span each (Valkey SET, Valkey ZRANGEBYLEX,
Valkey GETBUFFER, Valkey EVAL, …) carrying db.system.name,
db.operation.name, and the key. Pipeline flushes emit
Valkey pipeline writeFiles / deleteFiles / fetchMetadata / fetchHashes
with the batched command count. The lock emits
valkey-datastore lock acquire / release / withLock / inspect /
forceRelease, with acquire recording wait duration and whether it contended.
The sync service emits valkey-datastore pullChanged / pushChanged /
hydrateFile / preparePush / commitPush with file counts, path counts, the
remote sequence number, and fast-path indicators.
Added: Lock contention retries are recorded as retry span events on the
acquire span.
Added: Pipeline spans report valkey.pipeline.failed_commands and are
marked as errors when any command in the batch failed. pipeline.exec()
resolves with per-command errors rather than rejecting, so a partially failed
batch would otherwise have left a span reporting success. pullChanged also
reports datastore.files_skipped for paths whose metadata read failed, which
were previously dropped silently.
Changed: pushChanged and commitPush now report
datastore.files_pushed and datastore.files_deleted separately. applyChanges
returns a single count covering writes and deletes together, and reporting that
as a file count would have overstated pushes whenever tombstones were involved.
The value returned to callers is unchanged.
Changed: Nothing else observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the
TracerProvider, and every span is a no-op when none is registered.
Note: Spans are placed on the round trips that carry latency — index range
scans, blob reads, and pipeline flushes — rather than on every one of the
seventeen command call sites. Heartbeat renewals are deliberately uninstrumented
so a long-held lock does not bury the trace in periodic PEXPIRE spans.
Note on secrets: Blob values are file content and the connection URL embeds a password. Neither is ever recorded; span attributes carry only command names, key names, and counts.
2026.07.27.1
Changed: Bump @opentelemetry/api 1.9.0 → 1.9.1
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. Valkey round
trips emit one span each (Valkey SET, Valkey ZRANGEBYLEX,
Valkey GETBUFFER, Valkey EVAL, …) carrying db.system.name,
db.operation.name, and the key. Pipeline flushes emit
Valkey pipeline writeFiles / deleteFiles / fetchMetadata / fetchHashes
with the batched command count. The lock emits
valkey-datastore lock acquire / release / withLock / inspect /
forceRelease, with acquire recording wait duration and whether it contended.
The sync service emits valkey-datastore pullChanged / pushChanged /
hydrateFile / preparePush / commitPush with file counts, path counts, the
remote sequence number, and fast-path indicators.
Added: Lock contention retries are recorded as retry span events on the
acquire span.
Added: Pipeline spans report valkey.pipeline.failed_commands and are
marked as errors when any command in the batch failed. pipeline.exec()
resolves with per-command errors rather than rejecting, so a partially failed
batch would otherwise have left a span reporting success. pullChanged also
reports datastore.files_skipped for paths whose metadata read failed, which
were previously dropped silently.
Changed: pushChanged and commitPush now report
datastore.files_pushed and datastore.files_deleted separately. applyChanges
returns a single count covering writes and deletes together, and reporting that
as a file count would have overstated pushes whenever tombstones were involved.
The value returned to callers is unchanged.
Changed: Nothing else observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the
TracerProvider, and every span is a no-op when none is registered.
Note: Spans are placed on the round trips that carry latency — index range
scans, blob reads, and pipeline flushes — rather than on every one of the
seventeen command call sites. Heartbeat renewals are deliberately uninstrumented
so a long-held lock does not bury the trace in periodic PEXPIRE spans.
Note on secrets: Blob values are file content and the connection URL embeds a password. Neither is ever recorded; span attributes carry only command names, key names, and counts.
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. Valkey round
trips emit one span each (Valkey SET, Valkey ZRANGEBYLEX,
Valkey GETBUFFER, Valkey EVAL, …) carrying db.system.name,
db.operation.name, and the key. Pipeline flushes emit
Valkey pipeline writeFiles / deleteFiles / fetchMetadata / fetchHashes
with the batched command count. The lock emits
valkey-datastore lock acquire / release / withLock / inspect /
forceRelease, with acquire recording wait duration and whether it contended.
The sync service emits valkey-datastore pullChanged / pushChanged /
hydrateFile / preparePush / commitPush with file counts, path counts, the
remote sequence number, and fast-path indicators.
Added: Lock contention retries are recorded as retry span events on the
acquire span.
Added: Pipeline spans report valkey.pipeline.failed_commands and are
marked as errors when any command in the batch failed. pipeline.exec()
resolves with per-command errors rather than rejecting, so a partially failed
batch would otherwise have left a span reporting success. pullChanged also
reports datastore.files_skipped for paths whose metadata read failed, which
were previously dropped silently.
Changed: pushChanged and commitPush now report
datastore.files_pushed and datastore.files_deleted separately. applyChanges
returns a single count covering writes and deletes together, and reporting that
as a file count would have overstated pushes whenever tombstones were involved.
The value returned to callers is unchanged.
Changed: Nothing else observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the
TracerProvider, and every span is a no-op when none is registered.
Note: Spans are placed on the round trips that carry latency — index range
scans, blob reads, and pipeline flushes — rather than on every one of the
seventeen command call sites. Heartbeat renewals are deliberately uninstrumented
so a long-held lock does not bury the trace in periodic PEXPIRE spans.
Note on secrets: Blob values are file content and the connection URL embeds a password. Neither is ever recorded; span attributes carry only command names, key names, and counts.
updated labels
2026.07.21.1
Changed: Bumped ioredis from 5.6.1 to 5.11.1.
Upgrade note: No behavioral changes. Routine dependency maintenance.
2026.07.18.2
Changed: Version bump only, no code changes.
2026.07.18.1
Changed
- Pinned the
zodimport specifier tonpm:zod@4.4.3(wasnpm:zod@4) for hermetic dependency resolution. No runtime behavior change.
2026.07.05.5
Fixed
collectFullWalkDiffsilently discarded the truncated flag fromallPaths(), causing deletions beyond the 50k cap to be invisible. Now throws when truncated, refusing to run an incomplete diff.applyChangespipeline errors were silently swallowed —pipeline.exec()returns per-command[Error, result]tuples that were never inspected. Dirty state was cleared despite partial write failures. Now inspects every result tuple and throws with the list of failed paths.allPaths()truncation check used a separateZCARDafterZRANGEBYLEX(TOCTOU race). Replaced with LIMIT+1 pattern: fetch one extra entry and check length.createRedisClientTLS CA read: wrappedreadTextFileSyncwith contextual error message for missing CA files.
2026.07.05.4
Fixed
collectOneRelDiffstill used ZRANGEBYLEX prefix scan when a file was deleted locally (stat === nullfell through to the else branch). Inverted the branch: prefix scan only forstat?.isDirectory, ZSCORE point lookup for everything else (existing files and deleted files).collectOneRelDiffdirectory branch had no LIMIT clause — added 50k cap matchingallPaths/pathsForPrefixes.- Removed dead
deleted === "true"branch inpullFiles—applyChangeshard-deletes viaredis.del/zrem, so no meta withdeleted:trueis ever written.
2026.07.05.3
Fixed
- Lock
acquire()assigned nonce before the retry loop; ifredis.set()threw (e.g. ElastiCache failover), nonce was left set, permanently blocking re-acquire. Now uses a candidate variable and only promotes to nonce on success. pathsForPrefixesusedcharCode + 1inclusive upper-bound, which could match an adjacent path (e.g.data/foo0when scanningdata/foo/). Aligned to the 0xFF exclusive sentinel used incollectOneRelDiff.- Removed
RELEASE_NOTES.mdfrom manifestadditionalFilesper project policy. - Aligned
sidecar.tsisTraversal("")to returntrue, matchingmod.tsbehavior.
2026.07.05.2
Fixed
collectOneRelDiffused ZRANGEBYLEX prefix range for single-file dirty pushes, which over-fetched sibling paths (e.g. v1 matching v10, v11) and tombstoned them. Now usesZSCOREpoint lookup for files; range scan only for directories.- Verifier called
redis.connect()explicitly, which throws on an already-connected client after sync operations. Removed — ioredis lazy connect triggers on first command. allPaths()andpathsForPrefixes()were unbounded. Added 50k LIMIT clause and truncated flag.- Pinned zod to exact version (4.4.3) in deno.json.
Added
RELEASE_NOTES.mdfor CI publish workflow.- Regression test: verifier works after sync operation (double-connect).
2026.07.05.1
Added
- Valkey/Redis datastore extension for swamp
- Sorted-set path index (
ZRANGEBYLEX) for O(log n + k) prefix lookups - SET NX with Lua-guarded release for distributed locking
- Two-phase sync (
preparePush/commitPush) to minimize time under global lock - Sidecar dirty-path tracking with sequence-counter fast path for pull
- TLS support for AWS ElastiCache Serverless and MemoryDB
- Configurable key prefix, database number, connection timeout, and retry limits
2026.07.18.1
Changed
- Pinned the
zodimport specifier tonpm:zod@4.4.3(wasnpm:zod@4) for hermetic dependency resolution. No runtime behavior change.
2026.07.05.5
Fixed
collectFullWalkDiffsilently discarded the truncated flag fromallPaths(), causing deletions beyond the 50k cap to be invisible. Now throws when truncated, refusing to run an incomplete diff.applyChangespipeline errors were silently swallowed —pipeline.exec()returns per-command[Error, result]tuples that were never inspected. Dirty state was cleared despite partial write failures. Now inspects every result tuple and throws with the list of failed paths.allPaths()truncation check used a separateZCARDafterZRANGEBYLEX(TOCTOU race). Replaced with LIMIT+1 pattern: fetch one extra entry and check length.createRedisClientTLS CA read: wrappedreadTextFileSyncwith contextual error message for missing CA files.
2026.07.05.4
Fixed
collectOneRelDiffstill used ZRANGEBYLEX prefix scan when a file was deleted locally (stat === nullfell through to the else branch). Inverted the branch: prefix scan only forstat?.isDirectory, ZSCORE point lookup for everything else (existing files and deleted files).collectOneRelDiffdirectory branch had no LIMIT clause — added 50k cap matchingallPaths/pathsForPrefixes.- Removed dead
deleted === "true"branch inpullFiles—applyChangeshard-deletes viaredis.del/zrem, so no meta withdeleted:trueis ever written.
2026.07.05.3
Fixed
- Lock
acquire()assigned nonce before the retry loop; ifredis.set()threw (e.g. ElastiCache failover), nonce was left set, permanently blocking re-acquire. Now uses a candidate variable and only promotes to nonce on success. pathsForPrefixesusedcharCode + 1inclusive upper-bound, which could match an adjacent path (e.g.data/foo0when scanningdata/foo/). Aligned to the 0xFF exclusive sentinel used incollectOneRelDiff.- Removed
RELEASE_NOTES.mdfrom manifestadditionalFilesper project policy. - Aligned
sidecar.tsisTraversal("")to returntrue, matchingmod.tsbehavior.
2026.07.05.2
Fixed
collectOneRelDiffused ZRANGEBYLEX prefix range for single-file dirty pushes, which over-fetched sibling paths (e.g. v1 matching v10, v11) and tombstoned them. Now usesZSCOREpoint lookup for files; range scan only for directories.- Verifier called
redis.connect()explicitly, which throws on an already-connected client after sync operations. Removed — ioredis lazy connect triggers on first command. allPaths()andpathsForPrefixes()were unbounded. Added 50k LIMIT clause and truncated flag.- Pinned zod to exact version (4.4.3) in deno.json.
Added
RELEASE_NOTES.mdfor CI publish workflow.- Regression test: verifier works after sync operation (double-connect).
2026.07.05.1
Added
- Valkey/Redis datastore extension for swamp
- Sorted-set path index (
ZRANGEBYLEX) for O(log n + k) prefix lookups - SET NX with Lua-guarded release for distributed locking
- Two-phase sync (
preparePush/commitPush) to minimize time under global lock - Sidecar dirty-path tracking with sequence-counter fast path for pull
- TLS support for AWS ElastiCache Serverless and MemoryDB
- Configurable key prefix, database number, connection timeout, and retry limits
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned