5.3 Release Notes
Patch Releases
All patch release notes for 5.3.x are available on the releases page.
Subscription Catch-up
Table subscriptions now skip superseded record updates during catch-up with startTime or previousCount, matching live delivery. A record updated at versions 2, 3, and 4 is delivered only at version 4 if that version is still current. The comparison uses the stored primary metadata version: a later publication can suppress a preceding mutation, and removing a record or tombstone makes its mutations ineligible. JavaScript consumers that need retained mutation history, including these cases, must set includeSuperseded: true. Raw-event subscriptions retain their existing default; rawEvents: true includes superseded events unless includeSuperseded: false is explicit.
MQTT durable subscriptions with QoS 1 or 2 explicitly include superseded versions in live delivery and reconnect catch-up, preserving intermediate retained publications. Non-retained published messages remain independent and are not suppressed by the table version check. See Subscription options and migration and MQTT durable sessions.
Table subscriptions can also identify the origin node of each event: subscribe({ includeOrigin: true }) adds nodeId and nodeName to every event built from an audit record, so a JavaScript consumer can keep one cursor per origin node — the shape that replication resumes with — instead of a single cursor over the serving node's merged stream. See Origin identity.
Node Identity
node.hostname and replication.hostname must now be a bare hostname or IP literal. This value is the node's identity — it becomes the node's TLS certificate common name and the host that replication advertises to peers and dials to reach the node — so a URL or host:port value corrupted certificate matching and replication without surfacing an error: a node configured as http://host:9926 advertised and dialed a host literally named http (harper#2218).
Configuration validation now rejects these values at startup, so an existing install whose node.hostname or replication.hostname is a URL, host:port, or a non-string value will fail to start until it is corrected to a bare host. The startup error names the offending value and the reason it was rejected. A bare hostname (server-one), an IPv4 literal (10.0.0.5), and an unbracketed IPv6 literal (::1) are valid; a scheme, port, path, credentials, query string, fragment, bracketed IPv6 literal ([::1]), or non-string value is rejected. Route entries under replication.routes are unaffected and may still be URLs.
When node.hostname is unset, Harper resolves the identity to the first valid bare host among replication.hostname, the host in replication.url, the TLS certificate common name, and the Operations API host, falling back to 127.0.0.1. A derived source that is empty or unusable is skipped rather than failing startup.
Replication URLs now also bracket a bare IPv6 literal correctly (::1 becomes ws://[::1]:9933), which the URL parser previously rejected.
Component Deploys
Staged Build Retention
deploy_component builds each release under <componentsRoot>/.deploy-staging/<deploymentId> and validates it before swapping it live. A build that completes but is never activated now survives startup, bounded per component by the new deployment.stagingRetention.maxCount setting (default 5; 0 keeps none). Older builds are removed at the start of the component's next deploy and at startup; anything belonging to an in-flight or unsettled deploy is never touched. Groundwork for deploying from an already-staged build (harper#2315). See Configuration Options.
Security
Route-Owned Authentication
Harper now defers rejection of an unrecognized Basic or Bearer credential on the application HTTP port until a handler claims the request. Harper-owned routes still return the normal unauthorized response. Unowned routes can fall through to application middleware with the original Authorization header unchanged. This lets catch-all applications and reverse proxies apply their own authentication schemes without URL exemptions or header rewriting, and the unauthorized response such an application returns — including its own WWW-Authenticate challenge — is passed back to the client unchanged. Internal authentication failures continue to fail closed. See Authentication and route ownership.
Operation Allowlist Enforcement
A role's permission.operations allowlist is now checked before every other privilege check on the role, so an operation the list omits is denied whatever else the role carries. Table DDL and SQL previously went around it.
A role that relied on either will lose access on upgrade. Two cases to audit, both fixed by adding the operation to the list:
operationscombined withstructure_user—create_table,create_attribute,drop_table, anddrop_attributenow have to be listed.structure_userstill scopes them to its databases.operationsomittingsql— the role can no longer run SQL.
Roles with no operations field are unaffected, as are roles that already list everything they call. See Operation Permissions.
OIDC Trusted Publishing
A CI runner can now authenticate to Harper with no stored credential. It presents an identity token minted by its own provider, and if that token verifies against a trust policy configured on the instance, Harper returns a one-hour operation token for the user the policy names — the same exchange npm, PyPI, and AWS STS AssumeRoleWithWebIdentity use. This replaces a 30-day refresh-token secret with a rule you configure once and revoke with drop_oidc_trust.
Policies are managed with add_oidc_trust, list_oidc_trust, and drop_oidc_trust (all super_user), and live in the replicated system.hdb_oidc_trust table. A policy pins the issuer, an instance-specific audience, and a set of claim constraints. For GitHub Actions those constraints must pin the repository, pin the workflow, and gate the ref — a policy naming only a repository and workflow is refused, because anyone who can push a branch could otherwise add the trusted workflow to it and mint a token. Issuers with no registered profile must pin sub, which makes Kubernetes service accounts, GCP service accounts, and SPIFFE identities work with no provider-specific configuration.
A policy can also carry an operations list, narrowing the minted token to a subset of its user's role on the Operations API and SQL paths — narrowing only, and not covering an application's REST/GraphQL data path, so the policy's user should still be a least-privilege role for the data it can reach.
Identity tokens are single-use, though the replay record replicates asynchronously, so two simultaneous replays against different nodes can both succeed. Exchanges are recorded in the authentication audit stream when logging.auditAuthEvents.logSuccessful / logFailed is enabled — they are off by default, so turn them on before you need the trail. Every rejection returns the same message, with the specific reason written to the oidc-trust log. In the CLI, the exchange ranks below every configured credential, so enabling it does not silently re-point a pipeline that still has a token secret set. See OIDC Trusted Publishing and Workload identity.