Skip to content

v5.3.0-alpha.1

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Sep 20:18
· 50 commits to main since this release
e68efe9

First alpha of the Harper 5.3 line. It covers everything merged to main since v5.2.4, which is where the v5.2 patch branch was cut — fixes released as 5.2.5 through 5.2.9 originated here and are included.

Application database branches

An application can now take a private, durable fork of a database, work in it, and have it torn down with the component.

  • A component gets its own branch of a base database, with the base's blob tree cloned in by hard link rather than copied (#2352).
  • Tables can be declared into a branch through @table, ensureTable and defineTable (#2523).
  • drop_component removes the branched databases the application created (#2517).
  • A branched database no longer silently discards its most recent writes when the node is restarted after a crash or hard kill (#2414).
  • Branch identity is now owned from disk rather than from memory alone: a branch can reclaim an identity held by its own stranded roots, survives a bad marker instead of bricking, holds identity through cleanup, and deletes only the roots it recorded. create_database is closed to a branch identity so it cannot clone the wrong volumes, and an incomplete blob-volume clone is refused rather than adopted.
  • An empty branchedDatabases declaration loads under LMDB (#2413).

Data integrity

  • copy-db no longer produces a silently corrupt, non-restorable copy (#2098) — a copy that looked successful could not be restored.
  • Transaction-log replay fail-stops at a corrupt frame and discards the transaction it truncated, instead of replaying past the damage (#2087).
  • A backup captures only whole blobs, substituting a marker for a blob still being written (#2265), so a backup taken during a write is no longer half a blob.
  • Resolved attribute values are kept out of durable records at every layer that writes them (#2368) — previously a computed value could be persisted as if it were stored data.
  • An audit entry's field offsets no longer depend on its previousVersion value (#2499), and record version and transaction-log key are exposed as separate audit clocks (#2497).
  • getRecordAtTime walks back from the audit-store key rather than the record version, so partial-record history resolves correctly under LMDB.
  • Blob content created from a plain Uint8Array decodes as text rather than as byte values (#2415).
  • Boot fails when the data-version stamp was not recorded, rather than continuing against an unknown on-disk version (#2398).

Transactions, locking and storage

  • Writes issued after a mid-scope commit are atomic with the scope that owns them (#2239); a self-committing transaction in the context slot now starts a real scope (#2325); an ImmediateTransaction commits the handle it opens during its own commit (#2291).
  • A transaction parked in its commit phase is no longer poisoned (#2086) — this was destroying the deploy payload blob.
  • The RocksDB transaction handle is released on abort and on a failed direct commit (#2128).
  • Request-path commit conflict retries are bounded by the request's queue-time budget (#2459), and the exclusive update-attributes lock wait is bounded and released structurally (#2252).
  • A wedged commit now reports the long-lived transaction holding it up (#2473).
  • table.lock(id) provides exclusive record locks serialized across worker threads (#2462).
  • The RocksDB WriteBufferManager no longer stalls every writer by default (#2492).
  • Audit retention is applied continuously to RocksDB transaction logs (#2338), and log_files_deleted is reported instead of discarded (#2472).
  • Harper boots when its storage volume is full or at quota, instead of failing at startup exactly when you need it to diagnose the problem (#2245).
  • File-backed blobs support opt-in deflate compression (#2460).
  • A database can be opened into a caller-owned table graph instead of the global map (#2285).

Query engine and APIs

  • The query planner uses storage-level statistical range estimates (#2163), with a guard for the condition estimates a zero entry-count estimate breaks (#2479).
  • SQL configuration is wired to the real config: sql.engine, allowFullScan, maxSortRows, maxHashRows (#2484) — these settings previously had no effect.
  • Indexed array-element scans return each record once, before paging (#2493); element-scoping semantics for queries over array-valued properties are pinned (#2437).
  • REST supports total-count pagination via Prefer: count= with a Content-Range response (#2147), and the query-string parser no longer falls through from group-by into sort (#2397).
  • New put operation, plus a fix for the target-database authorization mismatch (#2347).
  • The Operations API resolves @relationship attributes instead of returning null or rejecting them (#2302).
  • set_configuration rejects unrecognized parameters instead of reporting success (#2272); install_node_modules honors the documented dry_run flag (#2340); list_agent_sessions orders by activity time (#2271).
  • An invalid GraphQL schema fails on its parse error instead of gating every application for 30 seconds (#2432).
  • MCP no longer manufactures create_* tools for Resources with no real create verb (#2405).
  • HNSW auto-scales efConstruction and the search-ef ceiling with graph size, for graphs of 1M+ nodes (#2181).
  • Per-table record-structure dictionary size is observable (#2250).

Security and authentication

  • A SQL permission denial computed by processAST is honored rather than dropped (#2202).
  • Under fail-closed mTLS, a client whose revocation status cannot be checked is rejected, and the issuer is recovered from Harper's trusted CAs when the socket chain lacks it. A certificate is never resolved as its own issuer, and an unchanged CA set is not republished.
  • TLS state is published transactionally, so a failed rebuild cannot downgrade below the last-good configuration (#2384).
  • Scoped authentication tokens: an inline role on create_authentication_tokens (#2176).
  • A component-registered operation can be granted in a role's operations allowlist (#2260).
  • An unrecognized app-port credential is rejected only once route ownership is known (#2419).
  • liveSubscriptionAuth can revoke a single subscriber without ending its subscription (#2039).
  • OIDC trusted publishing: deploy from CI with no stored credential (#2173).

Clustering and replication

  • A table being created stays invisible to catalog scans on other threads, so replication can never announce a partial attribute list (#2381).
  • Cluster-origin table definitions are additive-only, so a peer's partial schema snapshot cannot destroy locally declared attributes (#2258).
  • A non-bare-host node identity is rejected, and IPv6 replication URLs are formed correctly.
  • sourcedFrom cache-fill conflict convergence is fixed (#2065).
  • RocksDB subscription events are delivered for source fills whose version differs from the log key.

Server, workers and deployment

  • HTTP startup recovers after a pre-ready worker restart (#2129), and a pre-ready worker's event loop stays alive through startup (#2314).
  • A worker counts as replaced when its replacement is serving, not when it exits (#2363); worker respawns no longer block container restarts (#2316).
  • A node stays bootable when threads.maxHeapMemory is set below what a worker can start on (#2290).
  • A swallowed uWS startup-listen failure is surfaced instead of hanging silently (#2112); startup URLs are no longer double-wrapped when node.hostname is itself a URL (#2219); the startup banner reports the Harper version.
  • get_status counts all live worker threads in its cross-thread aggregation (#1952), and a stuck worker's OS thread state is logged when an ITC broadcast times out (#2521).
  • Component installs no longer hang at startup on zombie process groups (#2085).
  • A component deploy builds the replacement aside and validates it before the swap (#2345), waits for the restart it triggers, and tells MQTT clients why a publish was refused (#2341); components are restored after a failed deployment preparation (#2066).
  • The concurrent PATCH route fails fast when it is not ready (#2204).
  • On Bun, a streamed HTTP response closes when the client asks for it, fixing hung iterable REST responses (#2351).
  • The SSE serializer distinguishes an absent data/id/retry from a falsy one (#2096).
  • MQTT shares payload and QoS 0 packet encoding across topic subscribers (#2040).
  • The models config block hot-reloads when the config file changes (#2377).

Logging

  • The no-config window at startup no longer drops every log line (#2467).
  • External and component loggers inherit the main rotation config (#1877, #1880), and rotated archives are named after their source log so they cannot collide.

Windows

  • Intermittent set_configuration 500s caused by the config write's rename retry are fixed (#2339).
  • Watch paths are canonicalized so an 8.3 short path cannot abort the process (#2309), and a deleted watched path no longer raises an uncaught EPERM (#2364).
  • Component archive routing preserves explicit directory links, stays nonblocking, and avoids unnecessary archive installs.

Also in this release

Test and CI work: unit-test runs isolated from the installed Harper root (#2299); promoted QA regression anchors (MQTT connect wedge, EAV blob type drift, TLS cert-table swap, deployment payload operations, SSE finite-generator completion, transaction-log purge blast radius, copy-db blob store); deflaked caching, HNSW routing, record-count and indexed-TTL suites; a hardened Windows unit-test gate; CI job and step timeouts; AI-review workflow and concurrency updates; release backports targeted by PR milestone instead of the patch label; and dependency updates (alasql 4.19.0, a deduped nested msgpackr under rocksdb-js, and non-major bumps).

Full changelog: v5.2.4...v5.3.0-alpha.1