Skip to content

Releases: HarperFast/harper

v5.2.0

Choose a tag to compare

@github-actions github-actions released this 01 Aug 02:19
c11e097

Harper 5.2.0 is the first stable release of the 5.2 line. These notes cover everything on the 5.2 line since it branched from 5.1 — roughly 210 merged PRs across two months — not just the changes since the last beta. Fixes that were also cherry-picked onto the 5.1 patch train (5.1.16 through 5.1.26) are included here as well, since they are part of 5.2.0.

Headline work: a new SQL engine built on the Resource API is now the default, secrets get a first-class store and end-to-end custody, row-level read authorization is unified, RocksDB databases gain managed backup/restore, and a large cluster of transaction and storage correctness fixes closes several write-loss paths.

Upgrade notes

  • The SQL engine default changed from legacy to auto (#1285). Queries are now planned by the new Resource-API engine, with automatic fallback to the legacy AlaSQL path for shapes it does not support. Set sql.engine: legacy to restore the previous behavior, or new to disable fallback and surface unsupported shapes as errors.
  • Operation-scoped authorization is evaluated once per operation again (#1915, #1842). 5.2 alphas briefly evaluated allowRead/write hooks per record; that is reverted to the pre-5.2 contract. Applications that want row-level narrowing should use the new explicit rowFilter(record, context) and eventFilter(event, context) predicates.
  • @expiresAt now takes precedence over the table-level expiration default (#1812). Tables that set both will see per-record expiration win.
  • threads.count defaults to 1 on macOS and Windows (#1605). Neither platform has working SO_REUSEPORT, so additional HTTP workers could never share the server ports. An explicit threads.count still overrides.
  • TCP keep-alive delay is now 10 minutes. Socket noDelay/keepAlive options were never actually applied to TCP or UDS listeners, and the keep-alive delay was 600 ms rather than the intended 10 minutes (#1859).
  • Safe mode disables worker preload modules (#1848).
  • uWebSockets.js is opt-in for npm consumers (#1919). It remains bundled in the official Docker images.
  • If you ran storage.migrateOnStart on any 5.2 alpha or beta, migrated records were written without their version metadata — see "LMDB to RocksDB migration" below for the verification step.
  • First boot applies a data migration; rolling back to 5.1.x requires CONFIRM_DOWNGRADE=yes (#2046). Starting 5.2.0 against an existing store creates system.hdb_secret and records the data version as 5.2.0. The migration is additive and 5.1.x can still run the store, but a 5.1.x binary asks for confirmation before starting against data marked newer — and with no interactive terminal (systemd, containers, CI) that prompt currently blocks with nothing in the log. To downgrade, set CONFIRM_DOWNGRADE=yes in the environment (or pass --CONFIRM_DOWNGRADE yes); take a backup first.

SQL engine on the Resource API

  • A new SQL engine, built on the Resource API, is now the default (#1285). It plans against Harper's own indexes and resources instead of the legacy AlaSQL path, and sql.engine: auto falls back to legacy automatically for query shapes it does not support. Notable behaviors: a two-sided primary-key range is fused into a single bounded seek; ORDER BY <primary key> with no WHERE is served from index order rather than a full ordered scan; UPDATE col = col ± N is applied as an atomic addition; null-valued conditions are served only on indexNulls indexes; unindexed WHERE conjuncts are residualized rather than pushed; NOT IN uses correct three-valued logic. DISTINCT aggregates and UPDATE SET on the primary key fall back to legacy.
  • Sorting on the primary key is served from primary-store order instead of a separate sort pass (#1844).
  • Query planning no longer mutates the caller's conditions (#1911), so a reused condition object is not corrupted by planning.
  • Schema-unqualified SQL is authorized against the table the engine actually resolves (#1961) — see Security below.
  • An A/B benchmark comparing the new engine against legacy is now in the repo (#1845).

Security

  • Schema-unqualified SQL bypassed table permission checks (#1961). The authorization layer derived the affected schema/table set from the AST's databaseid; when a statement omitted the schema qualifier that field was empty, nothing was recorded in the affected-attribute map, and hasPermissions iterating an empty map authorized by vacuous truth — while the engine's binder resolved the same bare name to a concrete database and executed against it. Authorization now runs against the table the engine resolves, per table reference rather than once per statement. The same series records GROUP BY/HAVING columns, reports derived JOIN sources that carry no join.table, and refuses UNION/EXCEPT/INTERSECT/PIVOT/UNPIVOT outright rather than letting them pass unchecked.
  • allow* hooks now fail closed when they throw or reject (#1489). A hook that threw was previously treated as a pass.
  • ReDoS in config validation (#1784). A crafted directory path could pin the CLI at 100% CPU; the path allow-list regex is replaced with a control-character denylist that also rejects C1 controls and Unicode line separators.
  • Raw Error objects are no longer logged from REST (#1737), and logger arguments are auto-wrapped with a diagnostic property allowlist (#1749) — both closed paths where secrets could reach hdb.log.
  • Structured-logging sanitization gaps closed (#1994). Sanitization could invoke a live object's Proxy traps or getters, leak function/opaque-builtin properties, or throw inside its own fallback. inspectForLog/deepSanitizeErrors are now realm-safe, bounded in breadth and depth, and fail closed at the cap.
  • MCP verb-tool listings no longer leak to unauthorized sessions (#1943).
  • Reserved role-permission names are rejected as database names, and cluster_user handling is completed (#1913).
  • http.securityHeaders config added, and the authentication middleware is now named in the chain (#1568).
  • PACKAGE_ROOT is canonicalized so it matches realpath'd allowedPath checks (#1905), and npm pack --ignore-scripts is gated on install_allow_scripts alone (#1819).
  • enableProxyProtocol header buffering has a stall-timeout guard (#1947), so a peer that opens a connection and never completes the PROXY header cannot hold it open.

Secrets management

  • hdb_secret store with grant-scoped secret operations (#1554). Secrets are stored in a dedicated table with a pure envelope codec, serialized row mutations, validator caps, and grant-set semantics; secret operations are kept off the MCP default-allow surface.
  • Component .env files are protected in the operations API (#1527) and can be written via set_component_file, with an encrypted enc:v1 contract and a dormant decrypt hook (#1528).
  • Two-tier component secret delivery with env declarations (#1582), plus worker-spawn data providers and deferred env-secret decrypt replay so secrets reach worker threads correctly (#1559).
  • Live secret-change subscriptions and a live scoped accessor (#1787), with subscription teardown reference-counted by identity.
  • Config-shaping env vars arriving via component .env files are warned about loudly rather than silently ignored (#1580).
  • SSH deploy keys are decrypted to a transient file only for the git operation (#1795).
  • Registered operations can declare permissions for scoped delegation (#1599).

Access control

  • Record-scoped allowRead: unified row-level read access control (#1786, closing the second gap in #1422). Enforcement is consistent across reads, GraphQL checkPermission, and subscription delivery. Prefix and multi-record scans keep the awaited entry check; per-record enforcement is sync-only.
  • Live subscriptions are continuously re-authorized and revoked on permission loss or token expiry (#1535), with coverage extended to WebSocket, MQTT, and alter_role (#1634).
  • Row-level allowRead is enforced on custom mcpResources reads (#1839), which previously bypassed the check that equivalent REST reads applied.
  • Related-table allowRead binds to a proper resource instance (#1532).
  • Explicit rowFilter/eventFilter predicates (#1915) carry through filtered HNSW traversal, OR/range filtering, source-revalidated reads, subscription snapshots, replay, live events, and reload snapshots.
  • Audit records attribute registered-operation writes to the authenticated user (#1592).
  • Token login in core — a validated JWT can be exchanged for an httpOnly hdb-session cookie (#1546).

Managed RocksDB backups

  • RocksDB databases now have first-class server-managed backup and restore (#1831). New operations — create_backup, list_backups, verify_backup, delete_backup, purge_backups, restore_backup, and RocksDB support for get_backup — give incremental, checksum-verified backups under storage.backupPath, one subdirectory per database, including file-backed blobs and the transaction log. Everything is also runnable from the CLI, including offline against a stopped server. Restores serialize against a per-database lock/marker and verify the database is fully closed process-wide before purging and rewriting, so a crash mid-restore recovers cleanly instead of corrupting data.

Typed resources and the application model

  • Typed, discoverable resources (RFC 0001) (#1767): code-first defineTable plus a per-method request contract, with the six typed-resources exports wired into the component sandbox (#1825).
  • Applications can be routed by host and urlPath from the root config (#1964). Multiple applications can share a server while routing to distinct hosts or path prefixes. Mounts are enforced only at the routing boundary, fail closed on a wrong-typed config, and REST route regis...
Read more

v5.2.0-beta.4

v5.2.0-beta.4 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 31 Jul 03:42
fd4be16

This is a 5.2 beta. It is not recommended for production use.

Security

Schema-unqualified SQL bypassed table permission checks. The SQL authorization layer derived the affected schema/table set from the AST's databaseid. When a statement omitted the schema qualifier that field was empty, so nothing was recorded in the affected-attribute map — and hasPermissions iterating an empty map authorizes by vacuous truth. Meanwhile the v2 engine's binder resolved the same bare name to a concrete database and executed against it. Two independent name resolutions, one of which silently authorized nothing. Authorization now runs against the table the engine actually resolves, and is checked per table reference rather than once per statement.

The same series closes the surrounding gaps in the collectors: GROUP BY / HAVING columns are now recorded in the affected-attribute map, a derived JOIN source (which carries no join.table) is reported rather than skipped, and nested or compound queries the collectors were never able to record — UNION, EXCEPT, INTERSECT, PIVOT, UNPIVOT — are refused outright instead of passing through unchecked.

Operation-scoped authorization contract preserved. Two changes restore the pre-5.2 behavior rather than silently changing it:

  • Read (#1915): allowRead is evaluated once per operation, and the gate now runs before collection query planning, scans, or subscription audit setup — so an unauthorized caller can no longer trigger expensive work before being rejected. Applications that genuinely want row-level narrowing get explicit rowFilter(record, context) and eventFilter(event, context) predicates instead, which carry through filtered HNSW traversal, OR/range filtering, source-revalidated reads, subscription snapshots, replay, live events, and reload snapshots.
  • Write (#1842): one operation/collection verdict in default instance mode. For built-in loadAsInstance = false table handlers, each operation is gated by the permission hook that actually matches it — an array PUT calls allowUpdate once with the original batch before it starts, rather than per element.

Data integrity — LMDB→RocksDB migration

Migrated records lost their version and record prototype (#2014, fixes #2012). Every record written by storage.migrateOnStart since #1307 was stored without its [8-byte version][flags word] metadata prefix. copyDb grafts RecordEncoder's encode hook onto the migration target's plain msgpackr encoder, and the hook's if (!this.useVersions) opt-out read useVersions off that foreign encoder — undefined — so every migrated record took the non-versioned plain-encode path.

Downstream, prefix-less records decode without the metadata wrapper, so PrimaryRocksDatabase.getEntry skipped the structPrototype repair and point reads returned prototype-less plain objects: relationship getters, toJSON and getUpdatedTime all unreachable. Record versions were silently dropped, which also affects cache admission, ifVersion/CAS, and replication version comparison.

The fix writes the prefix correctly, adds a read-side repair for databases already migrated, stages the migration and renames it into place only after verification, and exports verifyMigratedDatabase(databasePath) so an existing installation can be checked. Verification sweeps every generation and exempts genuinely version-less records by key rather than by sniffing bytes.

If you have run migrateOnStart on any 5.2 alpha or beta, run verifyMigratedDatabase before relying on versions; a no-op rewrite pass is required to restore versions on already-migrated records.

TLS & certificates

  • MQTT's raw-socket listener now has its own TLS usage type, so it no longer shares certificate selection with the HTTP listeners (#1999, #2003).
  • The MQTT secure-port UDS metadata published an empty certificate list, which made a fronting SNI proxy fall back to serving the node certificate on 8883 (#2010).
  • A listener's TLS selector is no longer stranded when the system database has not finished loading, and the previous hdb_certificate subscription is properly ended on a table swap — previously a swap could leave a stale subscription feeding the selector.
  • The zero-certificates retry is keyed off the current pass rather than the persistent default context, so one empty pass can no longer poison later ones.

HTTP & networking

  • WebSocket upgrades were silently dropped on per-worker UDS mirror listeners (#2015). With tls.unixDomainSockets enabled, the per-worker UDS mirror is a separate http.Server that never received the 'upgrade' listener onWebSocket() attaches to the port-keyed server, so Node destroyed every WebSocket handshake on it with a zero-byte close — no response, no log. The same fix stops enableProxyProtocol()'s data interception from outliving the PROXY header decision: it was still forwarding post-upgrade frames to a freed HTTP parser that the parser pool can reissue to another connection, which produced verified cross-connection corruption.
  • TLS facts forwarded by a fronting proxy via PROXY v2 TLVs are now exposed as request.connectionInfo (#1985).
  • The operations API fails soft on a domain socket bind failure and warns on path-length overflow instead of failing to start (#1907).

Storage & audit log

  • The interrupted-drop retry is now bounded to one actionable error and scoped to a per-drop generation, keyed by physical store rather than database alias. A genuinely failed store drop is no longer reported as complete, reconcile sweeps every generation, and budget cleanup stays O(1) in the common case (#1957).
  • Audit cleanup has a real completion signal and a sane backoff, and a cleanup pass no longer escapes as an unhandled rejection (#1963).
  • LMDB audit entries now store the real prior version — the primary entry's own localTime rather than its origin version (#1988).

Components & resources

  • Concurrent component installs no longer corrupt dependencies. Lock reclamation is race-free, liveness checks are bounded, unconfirmed liveness can no longer renew the lock-wait deadline forever, and timed-out component preparation is handled explicitly (#1991).
  • Bare collection POST restores the v4 super.post create behavior, normalized before authorization (#1956).

CLI & operations

  • Token environment variables for CI/CD authentication, and harper login --for-ci to print CI credentials on stdout. Env tokens are gated on a remote target and userinfo is stripped from --for-ci output (#1876).
  • checkOverloaded() now logs once when it first starts rejecting writes, so a rejecting node is visible in the log instead of silently shedding load (#2007).

Also in this release

Migration to @harperfast/code-guidelines (#1992), a resource test teardown race fix (#1971), and TLS/SQL regression test coverage and deflaking across the changes above.

v5.1.26

Choose a tag to compare

@github-actions github-actions released this 31 Jul 04:14
43a375f

MQTT over TLS behind a fronting proxy

The MQTT secure-port UDS metadata published an empty certificate list when a plain TCP port was also registered (#2011). The metadata write read certificates off the wrong server, so a fronting SNI proxy had nothing to select on and fell back to serving the node certificate on 8883. Clients connecting to MQTT over TLS were presented the wrong certificate.

WebSocket upgrades on Unix domain socket listeners

WebSocket upgrades were silently dropped on the per-worker UDS mirror listeners (#2019). With tls.unixDomainSockets enabled, the per-worker UDS mirror is a separate http.Server that never received the 'upgrade' listener onWebSocket() attaches to the port-keyed server. Node destroys an upgrade socket that has no 'upgrade' listener — with no response and no log entry — so every WebSocket handshake arriving on the mirror failed silently, with nothing on either side to diagnose it. getHTTPServer() now exposes the mirror and onWebSocket() attaches the same upgrade dispatch to it.

The same change stops enableProxyProtocol()'s data interception from outliving the PROXY header decision. The wrapper kept forwarding post-upgrade frames to the HTTP parser it captured at connection time; once the connection upgraded, that parser was freed and could be reissued by the parser pool to an unrelated connection, which then received another connection's WebSocket frames. This was reproduced as cross-connection corruption. The wrapper now removes itself and restores the original 'data' listeners as soon as the header decision resolves.

Also in this release

Regression coverage for the UDS certificate-metadata and WebSocket-over-UDS paths.

v5.1.25

Choose a tag to compare

@github-actions github-actions released this 30 Jul 19:00
472cb17

TLS

A raw-socket TLS listener could permanently export an empty certificate list to a fronting proxy (#1998, #1999).

The certificate selector behind MQTT's network.securePort builds its SNI contexts from the hdb_certificate table and subscribes to that table for live updates. Two paths could leave it stranded with no certificates:

  • The selector could run before system.hdb_certificate was loaded on its thread. A component creates its listener — and this selector — without controlling database load order, so the pass completed against an absent table and nothing re-triggered it.
  • The table object could be replaced underneath it. A storage-engine migration (the table-by-table v4 LMDB → v5 RocksDB conversion during a live upgrade) or any resetDatabases()copy_db, ITC restart handling — installs a brand-new table object, orphaning the selector's subscription. Certificate changes after that point never triggered a rebuild.

In either case the listener published an empty certificates: list in its exported per-socket metadata and kept it, unchanged, until restart. A proxy that terminates TLS and routes by SNI from that metadata — Harper Fabric's Symphony — then had nothing to match on and served the instance's own node certificate for every connection to that port. This was confirmed on two of three nodes of an affected cluster, where every worker's exported MQTT metadata had been empty since container boot while HTTP listeners on the same workers were fully populated.

The selector now:

  • Tracks the certificate table by object identity rather than a "have we subscribed" flag, and re-subscribes — ending the orphaned subscription — whenever the table is swapped.
  • Leaves its readiness promise pending and retries on the normal debounce when the system database or the hdb_certificate table has not loaded yet, instead of resolving as though no TLS were configured.
  • Refuses to publish a completed pass that produced zero certificates, retrying instead. Transient single-use selectors are exempt, since certificate bootstrap depends on an empty result meaning "no certificate yet."
  • Skips a single unparseable certificate record rather than aborting the whole rebuild.

Both wait paths emit a one-time warning, so a listener stuck in this state is diagnosable from the log rather than only from a live node.

Full Changelog: v5.1.24...v5.1.25

v5.2.0-beta.3

v5.2.0-beta.3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 29 Jul 12:42
v5.2.0-beta.3
7422493

Third beta of the 5.2 line. 45 PRs since beta.2, weighted toward transaction correctness, authorization hardening, and CLI work.

Transactions & storage

The largest cluster of fixes in this beta. Several were write-loss paths.

  • Repeat writes to the same key within one transaction now layer correctly (#1970). A second write to a key applied against the pre-transaction value rather than the earlier write in the same transaction, so the intermediate update was lost.
  • Writes staged while a read iterator defers the commit are no longer dropped (#1860). Work queued during the deferral window was discarded when the commit finally ran.
  • ERR_TRY_AGAIN now retries on the same transaction using a native in-place reset (#1823), instead of forcing a fresh transaction.
  • Commit-retry exhaustion rejects the awaited request chain rather than resolving as if it had succeeded (#1861).
  • Honest Promise<number | void> type for the commit-latency recorder (#1853), and a matching recordCommitLatency parameter fix for the widened commitResolution union (#1899).
  • Table deletes on audit: false tables thread the transaction into removeEntry (#1869).
  • Clearer open-transaction timeout error message (#1967).

Secondary indexes

  • TTL eviction and delete no longer orphan secondary-index entries (#1896). Orphaned entries could satisfy later index reads for records that no longer existed.
  • Table.clear() clears secondary-index DBIs as well as the primary store (#1906).
  • Sorting on the primary key is served from primary-store order instead of a separate sort pass (#1844).
  • starts_with returns complete results for astral-plane Unicode values (#1887).

Authorization

  • Row-level allowRead is enforced on custom mcpResources reads (#1839). Custom MCP resources bypassed the row-level check that equivalent REST reads applied.
  • Reserved role-permission names are rejected as database names, and cluster_user handling is completed (#1913) — a malformed permission now returns 400 rather than validating clean, and cluster_user exclusivity is restored.
  • The built-in login resource awaits the request body before use (#1948).
  • URL attribute-suffix routing resolves correctly for programmatic static-properties Resources (#1933).

Replication

  • The resume-cursor write no longer freezes the apply worker (#1888). A blocking write on the resume cursor could stall apply indefinitely.
  • hdb_nodes reload markers no longer de-authorize live peers (#602, harper-pro).

Server & networking

  • PROXY protocol v2 decodes forwarded mTLS client certificates on UDS mirrors (#1858).
  • A stall-timeout guard bounds enableProxyProtocol's header buffering (#1947), so a peer that opens a connection and never completes the header can't hold it open.
  • Socket noDelay/keepAlive options were never actually applied on TCP or UDS listeners, and the keep-alive delay was 600 ms rather than 10 minutes (#1859).

CLI & agent

  • harper agent — a CLI client for the built-in agent (#626), with harper chat as an alias.
  • Expired agent tokens refresh automatically, and the --once approval hang is fixed.
  • Auth credentials are resolved as atomic pairs ahead of payload fields, separating transport auth from the operation payload for add_user/alter_user (#1873) — previously the two could be conflated.
  • The built-in agent drains operations tools from the lazy provider (#1847).

Deployments

  • get_deployment_payload and delete_deployment_payload operations are implemented (#1898).
  • Peer-side payload_blob reads retry on a transient 503 stall (#1838).
  • WAF is activated on upgraded instances (#1910).
  • Safe mode disables worker preload modules (#1848).

Packaging

  • The published shrinkwrap no longer carries the react-native tree (#1959); only optional react-native-fs edges are severed, and the result is verified.
  • uWebSockets.js stays opt-in for npm consumers (#1919) while remaining bundled in official images.
  • PACKAGE_ROOT is canonicalized so it matches realpath'd allowedPath checks (#1905).

MCP

  • The durable quota policy registers as a function rather than a config-referenced Resource (#1821).

Also in this release

Dependency updates (#1955, #1954), SQL engine A/B benchmark (#1845), a packaged-application E2E workflow (#1908), CI shard rebalancing and single-Node integration runs on PR pushes (#1883), a MAX_SET_TIMEOUT_MS constant hoist (#1816), and promoted QA regression anchors covering cross-version upgrade read-visibility, secondary-index integrity, typed-struct plateau on legacy-origin in-place upgrade, and transaction commit behavior (#1900, #1886, #1884, #1870, #1895).


Full Changelog: v5.2.0-beta.2...v5.2.0-beta.3

v5.1.24

Choose a tag to compare

@github-actions github-actions released this 28 Jul 22:48
v5.1.24
8f089da

Storage engine

Fixes silent missing or wrong rows on multi-table requests. Bumps @harperfast/rocksdb-js to 2.4.1, which carries the cross-column-family transactional read fix (rocksdb-js #717, backported to the 2.4 line in #732).

TransactionHandle::get honored the caller's column-family override on its synchronous block-cache-tier attempt but dropped it in the async worker, falling back to the transaction's own column family. Because all tables in a Harper database share one read transaction, every table after the first in a given request was read through a foreign column family. Reads that hit the block cache were correct; reads that missed it silently returned not-found — so the failure was intermittent and scaled with cache residency. A warm key set read correctly, a cold one lost most or all of its records, and the symptom was worst immediately after a restart and healed as traffic warmed the cache. Where two tables share a key format, a read could also hit in the foreign column family and return another table's row.

This is the defect behind #1881 (secondary-index reads returning partial or empty results for the second table accessed in a request).

Any deployment on rocksdb-js 2.4.0 is affected regardless of whether it currently looks healthy — steady traffic keeps keys warm and masks it. The dependency floor is now pinned so the fix cannot be lost to dependency resolution at image-build time; previously the range permitted a fixed version but the lockfile did not require one, so what shipped depended on when the image was built.

Security

Table read permissions are now enforced for subscriptions (#1914). Tables using loadAsInstance = false did not invoke allowRead before subscription setup, so a checked subscription could bypass table read permissions that the equivalent get would have enforced. Subscription setup now matches the get authorization contract.

This preserves v5.1's table-level permission semantics rather than backporting main's row-level delivery behavior.

Also in this release

CI: synced the claude-review.yml caller with main on the release branch.

Not in this release, despite appearing in the commit list: "fix(query): stop query planning from mutating the caller's conditions" (#1911) was cherry-picked onto the release branch and then reverted before the cut. Its commits and the revert both show up in the raw changelog below and cancel out — there is no query-planning behavior change in 5.1.24. That fix remains on main for 5.2.


Full Changelog: v5.1.23...v5.1.24

v5.2.0-beta.2

v5.2.0-beta.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 22 Jul 03:35
v5.2.0-beta.2
02da352

Pre-release (5.2.0-beta.2) for testing — not a stable release.

Features

  • Built-in scheduler component (#1828, #1866): config-declared cron/interval jobs that run once per cluster — leader election, failover, and catch-up (backfills the most recent missed occurrence), with in-repo election/failover/catch-up test coverage.
  • @expiresAt attribute (#1810): a per-record expiration field, now authoritative over the table-level expiration default; hardened coercion (Date / bigint / ISO string; ignores boolean/empty).
  • Transaction queue-depth metrics: write/read transaction queue depth (count) exposed in analytics (LMDB accounting guarded), alongside commit-latency.
  • Typed-resources sandbox exports (#1825).

Fixes

  • Actionable inactive-component 404 (#1806): a deployed-but-not-yet-restarted component's URL returns an actionable, super_user-gated 404 ("Harper may need to be restarted…") instead of a bare Not Found.
  • TLS ciphers/SECLEVEL honored from every configured source when building listeners (#1841).
  • CLI failure paths exit non-zero, including operation timeouts (#1801).
  • Clean 404 (not a crash) on a POST without a trailing slash; SSE writes guarded against undefined event data (#1724/#1863).

Also in this release

nextjs-adapter advisory CI gate (#1385); rocksdb-js 2.5.0; Node 24; QA read-consistency regression anchors (#1833); scheduler stored-error path redaction; non-major dep updates.

Full Changelog: v5.2.0-beta.1...v5.2.0-beta.2

v5.1.23

Choose a tag to compare

@github-actions github-actions released this 22 Jul 23:31
v5.1.23
00295c7

Reliability

  • Retry the peer-side payload_blob read on a transient 503 stall during replicated deploy_component (harper#1838 backport). A blob read that stalls under a transient condition (e.g. a replication WebSocket reconnect coinciding with in-flight blob-chunk delivery to a high-latency peer) is now retried within a ~120s budget instead of failing the deploy outright — confirmed against a real 15-node multi-region production cluster, where this was the correct and sufficient fix (not merely a mitigation).
  • Deduplicated deployment timeout coercion (internal cleanup, no behavior change).

Full Changelog: v5.1.22...v5.1.23

v5.2.0-beta.1

v5.2.0-beta.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 17 Jul 15:40
v5.2.0-beta.1
8f47d0d

Pre-release (5.2.0-beta.1) for testing — not a stable release. First 5.2 beta.

SQL engine (Resource API) — default flipped legacyauto (#1285)

The new SQL engine on the Resource API is now the default (phases 0-5 cutover); unsupported query shapes fall back to the legacy engine automatically. Highlights:

  • Two-sided primary-key range fused into one bounded seek (#1822).
  • No-WHERE ORDER BY <indexed> served via index order; restricted to the primary key to avoid a full ordered scan (D-219).
  • UPDATE col = col ± N applied as an atomic Addition (F-146).
  • Null-valued conditions served only on indexNulls indexes; runtime search rejections convert to fallback.
  • Unindexed WHERE conjuncts residualized instead of pushed; correct 3VL for NOT IN.
  • Unsupported cases (DISTINCT aggregates, UPDATE SET on the primary key) fall back to legacy.

Data-safety fixes

  • Reject null/undefined id in a Resource-API delete — previously could wipe the whole table — plus a field-aware validator message.
  • Reject null hash values on delete instead of wiping the table (#1837).
  • Reject changing a table's primary key when it has records.
  • SQL UPDATE/DELETE row-finder no longer drops not-yet-swept expired rows.

Also

  • search()'s includeExpired opt-out documented in the DESIGN cheat sheet.

Full Changelog: v5.2.0-alpha.6...v5.2.0-beta.1

v5.2.0-alpha.6

v5.2.0-alpha.6 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 17 Jul 01:10
v5.2.0-alpha.6
1df0df5

Pre-release (5.2.0-alpha.6) for testing — not a stable release.

Features

  • Typed, discoverable resources — RFC 0001 (#1767): code-first defineTable plus a per-method request contract.
  • harper agent CLI (#1549): a command-line client for the built-in agent (authz enforced via standard dispatch).
  • Live secrets (#1776): live secret-change subscriptions and live scoped accessors, with subscription teardown reference-counted by identity.

Access control

  • Record-scoped allowRead (#1422, #1419): unified row-level read access control — enforced on reads, GraphQL checkPermission, and subscription delivery, with subscriptions continuously re-authorized against the live user (#1414). Prefix/multi-record scans keep the awaited entry check; per-record enforcement is sync-only.

Security & reliability

  • ReDoS in config validation fixed (#1784): a crafted directory-path regex no longer pins the CLI at 100% CPU.
  • SSE mid-stream error handling (#1789): a generator that throws mid-stream no longer hangs the response or escapes as an uncaughtException.
  • SSH deploy keys encrypted at rest (#1795): decrypted to a transient file only for the git operation.
  • Analytics window fix (#1796/#1798): get_analytics reads from the bounded time window rather than the metric index.
  • Config env-precedence fix (#1618/#1726); QUERY-verb checkPermission bypass closed.

Deploy

  • General-purpose credentials array (#1797): deploy_component registryAuth reshaped into a reusable credentials array.

Also in this release

Broad QA regression-anchor promotions (static / deploy / shutdown-drain / secrets / subscription paths); build+shrinkwrap hardening (#1783); uWS-on-pointer-compression guard (#1765); analytics deflake (#1794).

Full Changelog: v5.2.0-alpha.5...v5.2.0-alpha.6