Skip to content

v5.3.0-beta.2

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 22 Sep 14:40
· 181 commits to main since this release
79e881d

This release covers changes since v5.3.0-beta.1. For everything in the 5.3 line since the last stable release, read the beta.1 notes as well. The core changes carried by this release are inlined below.

Certificates

  • add_certificate finds a private key that is already on the node. Installing a non-CA certificate failed with A suitable private key was not found for this certificate whenever the matching key existed on disk but not in the in-memory key map — the normal state after Harper auto-generates a CA key it writes as privateKey<uuid>.pem and no config names. The operation now reads the key files that certificate records name, the way signCertificate and createCsr already did, so it succeeds without the caller resubmitting the key. (#860)

Also in this release

Core pointer syncs bringing the Harper changes listed below (#870, #872, #873, #877, #880, #892); and two integration-test fixes — the directional-flow fixture is seeded before the core node exists (#875) and the unit-test root is removed only after its databases are closed (#893).

Full changelog: v5.3.0-beta.1...v5.3.0-beta.2


Harper (core) v5.3.0-beta.2

Released at HarperFast/harper v5.3.0-beta.2. Core changelog: v5.3.0-beta.1...v5.3.0-beta.2.

Security

  • SSH private keys no longer reach the logs. A private key passed to add_ssh_key or update_ssh_key was written verbatim to the core operations log and to the MCP audit log. Both redactors now apply an operation- and tool-scoped field policy, so the key is removed for those operations without hiding the same field name where it carries no secret (for example an environment variable's name). (#2741)
  • A rejected cookie or mTLS session is now a decision, not a raw error. A server.getUser override that rejected a cookie session escaped the authentication middleware as a stringified error. Those branches now behave like the Authorization branch: a failed principal resolution becomes a negotiated 401, or a deferred credential rejection the route owner settles, and a component can tell a rejected credential apart from an internal fault. (#2717, fixes #2703)

Replication and clustering

  • An out-of-order write below the audit-retention floor no longer stalls replication. Such a write entered the reconciliation walk, and the walk's head lookup fanned out across every per-origin transaction log. On a 16-node cluster this left two replication legs with zero commits for over 24 hours, with one HTTP worker per receiver pinned at 100% CPU and progress of roughly one record every few minutes. Below the floor a write now contributes only its commutative operations. (#2684)
  • A record key beginning with / no longer hangs the subscription broadcaster. notifyFromTransactionData walked / to / forever, pegging every HTTP worker that held a subscription on that table at 100% CPU with nothing in the log. The walk now stops when the computed parent equals the current key, and a subscriber on the root key is still notified once. The bug was present in 5.1.x and 5.2.x as well. (#2689)
  • An exhausted cluster lock wait reports what a home actually answered. A contended lock() with a short wait returned 423 or 503 LOCK_UNAVAILABLE at random, which matters because 423 means "held, retry" while 503 means cluster lock coordination is broken. A wait's terminal status is now taken from the last reply a home completed, bound to the route and generation that produced it; a deadline this node imposed on its own probe never classifies the wait. The loop also stops before issuing a probe that cannot complete within the remaining budget. (#2685)

Storage and recovery

  • The restoring marker is now published atomically. beginRestore() opened the marker with openSync(path, 'w'), truncating it before the database name was written, and scanBlockedRestores() skips a marker whose first line is empty. A crash inside that window left a half-purged database that loaded as healthy on the next boot. The marker is now written temp file, fsync, rename, parent fsync, and an intact marker is reused rather than rewritten. (#2644)
  • Blob paths stay stable for aliased databases. A shared physical store kept being restamped by later logical aliases, so blob-root resolution moved across reconciles. The identity assigned first in initStores() now wins. Compatibility note: a deployment with file-backed blobs stored under an alias that previously happened to load last will now resolve to the first-assigned identity; no persisted identity or blob migration ships with this change. (#2683)
  • rocksdb-js updated to 2.10.0. (#2720)

Analytics

  • Aggregation resumes from the last raw record it rolled up. The resume marker was stamped with the cycle's own end-of-run Date.now(), so every raw report that arrived between the end of the rolled-up window and the end of the cycle was skipped permanently: the remainder of a backlog left by a late tick, and anything a worker reported while the cycle ran. get_analytics under-counted by whatever fell in that gap and the samples never came back. The resume point is now a cursor that moves only to a record a cycle actually read, and stays put when a cycle reads nothing. (#2692)

Vector search

  • Native HNSW planes use about half the layer-0 memory. The layer-0 neighbour array reserved 128 u32 slots per node, derived from values nativePlane pins, so no index declaration could change it — while a live 7.7M-node plane carries a mean layer-0 degree of 29. The new per-index nativePlaneLayer0Cap option defaults to 64, which holds recall@10 within about 0.5 points of cap 128 at every ef ≥ 128 measured at 1M and 4M vectors, at the same latency. (#2701)
  • @harperfast/hnsw updated to 0.4.0, which brings a 32x cut in visited-set memory, kernel-level prefetch of non-resident slots, and a parallel batch build not yet consumed here. The on-disk plane format is unchanged, so existing installations need no reindex. (#2722)

Full-text search (groundwork, not yet reachable)

  • A native full-text derived-index backend converts committed record changes into @harperfast/fulltext mutation batches and publishes the Harper replay cursor with the Tantivy commit. Harper records and audit logs stay authoritative; the Tantivy files are rebuildable derived state. This is the backend slice only — schema activation, reader attachment, Table.search, REST exposure and status reporting are follow-up work, so nothing about it is reachable by an application yet. (#2569, part of #2510)

Also in this release

logger.closeLogFile now stays bound to the current file after a path reassignment, a latent hazard on public API surface whose only callers today are test fixtures (#2724); CI now requires a planning receipt on changes to core-shared storage paths (#2594) and the derived-index runtime is back under that policy (#2745); DESIGN.md is partitioned into per-directory design notes indexed from the root (#2728); test stabilisation for the analytics aggregation cycle (#2699), HNSW distance inversions (#2702) and unit-test database teardown (#2720); dependency updates for alasql 4.19.1 (#2698) and mocha 12 (#2700); and a refresh of the AI review prompt pin (#2610).