Skip to content

v5.3.0-beta.1

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 Sep 15:53
· 745 commits to main since this release
3560603

Harper 5.3.0-beta.1 is the first beta of the 5.3 line. It covers everything merged since the
last stable release, v5.2.13 — both 5.3 pre-releases and the fixes that also shipped on the
5.2 patch train.

Branched databases

An application can declare branchedDatabases: [data] and get a private, durable fork of that
database — its own rows, invisible to the base and to every other application, reached through
the same databases import (#2352). Several variants of an application can run against isolated
copies of the same data. The fork lives at a deterministic path derived from the application and
base database names, so it is adopted again on restart and resolves to the same place on every
node. Branches declare their own tables through @table,
ensureTable and defineTable (#2523), get their own blob store cloned with hard links so a
fork is cheap (#2426), and are removed with the application on drop_component (#2517). Two
correctness fixes came out of the same work: a branched database no longer silently discards
its latest writes when restarted after a crash or hard kill (#2414), and an empty
branchedDatabases declaration now loads under LMDB (#2413).

Record locks

table.lock(id) gives an application an exclusive lock on a single record. It is serialized
across worker threads on one node (#2462), then across the cluster with amortized per-record
ownership so a hot record does not pay a round trip per lock (#2498). Multi-threaded nodes
relay a local lock() to the owning worker (#2667). For cluster safety, successor freshness
is fenced by a lockBarrier control entry (#2627), successors are kept fresh across
cross-node handoffs (#2613), and a node can prove it has quiesced rather than waiting out its
delegation lease (#2663).

Vector search: native HNSW

HNSW indexes now run on a file-primary memory-mapped graph backend built on a shared
derived-index runtime (#2430, #2567). An eligible index defaults to that native plane when its
table has durable audit enabled, or enables audit: true in the same declaration; a table that
only inherits global audit configuration stays on the JS implementation, and nativePlane: false
is the explicit opt-out (#2657). Queries rerank only the rows they actually return and resolve hits from keys stored
in the plane, instead of reranking the whole candidate set (#2665). Index lag is bounded and
callers can wait for prior writes to be visible (#2658), including a durability barrier that
runs during catch-up rather than only when the write queue drains (#2676).

HTTP, REST and components

  • REST supports total-count pagination through Prefer: count=, answered with Content-Range
    (#2147), so a client can page without a second counting query.
  • withNodeAdapter now rebuilds its response on a real Writable, so Node middleware —
    Next.js, compression, send — runs against it correctly (#2528).
  • Streaming REST responses return structured errors instead of a truncated body, with the
    terminal harper-error frame pinned by regression coverage (#2614, #2617). On Bun, a
    streamed response is closed when the client asks for it, fixing hung iterable responses
    (#2351).
  • A new put operation was added, and the target-database authorization mismatch it exposed
    was fixed (#2347). The Operations API resolves @relationship attributes instead of
    returning null or rejecting them (#2302), and a component-registered operation can be
    granted in a role's operations allowlist (#2260).
  • Array PUT follows collection semantics, dispatches per element, and is atomic per batch
    (#2097). The REST query-string parser no longer falls through from group-by into sort
    (#2397), and the SSE serializer distinguishes an absent data/id/retry from a falsy one
    (#2096).
  • An application can be run in its own dedicated worker thread (#2524).
  • Component deploys got safer: a replacement build is assembled aside and validated before the
    swap (#2345), the deploy waits for the restart it triggers and tells MQTT clients why a
    publish was refused (#2341), a build can be staged with activate:false and deployed later
    by deployment_id (#2605), dormant staged builds are bounded by
    deployment.stagingRetention.maxCount (#2531), the activation swap retries through a
    transient rename lock (#2570), and components are restored after a failed deployment
    preparation (#2066). An invalid GraphQL schema now fails on its parse error instead of
    gating every application for 30 seconds (#2432).

Security

  • An operation is authorized against the authenticated principal that invoked it, closing a
    path where the wrong identity was checked (#2217).
  • SQL honors the permission denial that processAST computes rather than continuing past it
    (#2202).
  • create_authentication_tokens accepts an inline role, so a token can be scoped to less than
    the creating user's full authority (#2176).
  • Components can be deployed from CI with no stored credential, using OIDC trusted publishing
    (#2173).
  • mTLS clients whose revocation status cannot be checked are rejected under fail-closed, and
    the issuer is recovered from Harper's trusted CAs when the socket chain does not carry it
    (#2457). TLS state is published transactionally, so a failed rebuild can never downgrade the
    node below its last-good configuration (#2384).
  • liveSubscriptionAuth can revoke one subscriber without ending its subscription (#2039).

Data integrity and durability

  • Transaction-log replay fail-stops at a corrupt frame and discards the transaction that frame
    truncated, instead of applying a partial transaction (#2087).
  • copy-db no longer produces a silently corrupt, non-restorable copy (#2098), and a backup
    captures only whole blobs, substituting a marker for one still being written (#2265).
  • Resolved attribute values are kept out of durable records at every layer that writes them
    (#2368) — a stored record no longer inherits a computed value.
  • Cluster-origin table definitions are additive-only, so a peer's partial schema snapshot
    cannot destroy locally declared attributes (#2258), and a table being created is invisible to
    catalog scans on other threads, so replication cannot announce a partial attribute list
    (#2381).
  • Multi-worker counter increments are no longer lost, and a resequenced write reusing a version
    no longer produces stale reads (#2259).
  • Record version and txnLogKey are exposed as separate audit clocks (#2497), and an audit
    entry's field offsets are independent of its previousVersion value (#2499). A follow-up
    fixed LMDB partial-record history resolution (#2526).
  • Transaction handling: the RocksDB transaction handle is released on abort and on a failed
    direct commit (#2128), a transaction parked in its commit phase is not poisoned (#2086), an
    ImmediateTransaction commits the handle it opened during its own commit (#2291), a real
    transaction scope is started when the context slot holds a self-committing transaction
    (#2325), and a request-path commit's conflict retries are bounded by its queue-time budget
    (#2459).
  • Same-key write order is preserved for explicit saves (#2561), and a supplied record root is
    rejected rather than silently replacing the instance's staged changes (#2578).
  • Transaction-log space reclamation is guarded after a restart (#2529), log_files_deleted is
    no longer discarded (#2472), audit retention is applied continuously to RocksDB transaction
    logs (#2338), and a write-ahead retention floor is recorded and raised before every prune
    (#2458).
  • Harper keeps booting when its storage volume is full or at quota (#2245), and fails the boot
    when the data-version stamp was not recorded (#2398) rather than running against an
    unconfirmed upgrade.

Indexing

  • A secondary-index backfill resumes from its checkpoint and yields the event loop, so a large
    table converges instead of stalling the node (#2539), and every index write a checkpoint
    covers is awaited so a failed write cannot be skipped (#2563).
  • isIndexing is stamped on the schema load path, so no thread serves a partially built index
    (#2543).
  • Indexed array-element scans return each record once, before paging (#2493), and
    element-scoping semantics for queries over array-valued properties are pinned by tests
    (#2437).
  • searchByIndex and the custom-index search() contract take an options object instead of a
    positional tail (#2187). A stale positional fifth argument to searchByIndex now throws a
    TypeError rather than silently permitting a scan it meant to forbid, and a custom index's
    search() receives { filter, minResults } with context still second positional.

Query planning and performance

  • The query planner uses storage-level statistical range estimates (#2163), guards estimates
    that a zero RocksDB entry-count would turn into Infinity or a negative (#2479), and
    describe sizes record counts from a bounded estimate instead of a second full key scan
    (#2612).
  • The RocksDB WriteBufferManager no longer stalls every writer by default (#2492), and active
    scans stay alive during eviction (#2556).
  • Post-ordering sort keys are resolved once per row, and a multi-store chain renews its idle
    window at each native commit (#2675).
  • MQTT shares payload and QoS 0 packet encoding across the subscribers of a topic (#2040).
  • sql.engine, allowFullScan, maxSortRows and maxHashRows are wired to real config
    (#2484), and per-table record-structure dictionary size is observable (#2250).
  • Opt-in deflate compression for file-backed blobs (#2460).

Operations and diagnostics

  • Startup and restart: a swallowed uWS listen failure is surfaced instead of hanging (#2112),
    HTTP starts correctly after a pre-ready worker restart (#2129), a pre-ready worker's event
    loop stays alive through startup (#2314), a node stays bootable when threads.maxHeapMemory
    is set below what a worker needs (#2290), worker respawns no longer block container restarts
    (#2316), a worker counts as replaced when its replacement is serving rather than when it exits
    (#2363), and a job worker cannot disable the rolling-restart throttle (#2600).
  • Configuration: the root config is read synchronously with rename retries bounded by wall
    clock (#2191), set_configuration rejects unrecognized parameters instead of reporting
    success (#2272), the models block hot-reloads on file change (#2377), and intermittent
    Windows set_configuration 500s are fixed (#2339).
  • Diagnosis: the long-lived transaction holder behind a wedged commit is reported (#2473), a
    stuck worker's OS thread state is logged when an ITC broadcast times out (#2521), replicated
    apply failures are reported before later events are consumed (#2630), and the startup options
    banner reports the Harper version (#2263).
  • Logging: external and component loggers inherit the main rotation config (#1880),
    logging.rotation.maxSize is enforced on the write path rather than only on the 60-second
    audit tick (#2475), and the window before config load no longer drops every log line (#2467).
  • Node identity and URLs: a non-bare-host node identity is rejected and IPv6 replication URLs
    are formed correctly (#2223), and startup URLs are not double-wrapped when node.hostname is
    already a URL (#2219).
  • Windows: 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).
  • list_agent_sessions is ordered by activity time instead of primary-key order (#2271), MCP no
    longer manufactures create_* tools for Resources with no real create verb (#2405), an
    analytics sample stays recorded while its window is being flushed (#2662), and
    install_node_modules honors the documented dry_run flag (#2340).

Also in this release

Test, CI and maintenance work: promoted QA regression anchors and new integration coverage for
blob lifecycle, audit history, MQTT, SSE, TTL and caching; unit-test isolation from the
installed Harper root; per-suite CI steps with measured time budgets; Windows and Node 26 job
fixes; AI-review-coverage enforcement and review-workflow tuning; release-backport targeting by
milestone instead of the patch label; and dependency updates including rocksdb-js 2.9.1.