Version History
Version π§ Unreleased Latest
Major release with REST and GraphQL APIs, MCP integration for AI assistants, OpenTelemetry-based observability, and a ready-to-run monitoring bundle. Multiple breaking changes to config keys, endpoints, and metrics. Plan an upgrade window.
Highlights
- Documentation site β Browse guides, reference, and APIs at /docs/
- REST API + OpenAPI UI (Scalar) with ETag-based conditional requests β see the REST Channel Guide
- GraphQL API + GraphiQL β Flexible query API with Relay Node interface and dynamic schema generation β see the GraphQL Channel Guide
- MCP Channel β AI-ready integration for Cursor, Claude Desktop, VS Code, Windsurf β query data and monitor health from your IDE β see the MCP Channel Guide
- Turnkey monitoring bundle shipped (Grafana dashboards + Docker Compose + Helm chart) β see the Monitoring Guide
- OpenTelemetry-first observability with metrics inventory and migration mapping β see Observability
- Operational endpoints β
/observe/*for status, health, doctor/pressure, stats, and channel discovery β see Monitoring Guide - Query performance improvements (planner v2 + backpressure under load) β see also Backpressure Under Query Load
- Rule-based log filtering β suppress noise with glob patterns and LDAP-style filters β see Observability: Wide Logs
- FleX query language β flexible filter syntax for runtime queries and config β see Filters Reference
Breaking Changes
If youβre upgrading an existing deployment, start here: Upgrading IdentityScribe.
Environment Variable Prefix
BREAKING: Environment variable prefixes have been standardized.
IDENTITY_SCRIBE_*βSCRIBE_*SCRIBE_TELEMETRY_*βSCRIBE_*
Migration:
# OldIDENTITY_SCRIBE_READONLY=trueSCRIBE_TELEMETRY_TRACES_ENABLED=true
# NewSCRIBE_READONLY=trueSCRIBE_TRACES_ENABLED=trueLogger Configuration Consolidated
BREAKING: Logger configuration has been consolidated to 5 canonical loggers with standardized environment variable overrides. Old logger config keys are removed.
| Old Config | New Config | Purpose |
|---|---|---|
log.EventStore | log.Ingest | Transcription pipeline, event store |
log.Scribe | log.Ingest | Transcription pipeline |
log.Settings | log.Config | Configuration parsing |
log.LDAP | (removed) | Use segment/wide logging via Monitoring |
log.AsyncSearch | (removed) | Use segment/wide logging via Monitoring |
log.TaskExecutor | (removed) | Use segment/wide logging via Monitoring |
New canonical loggers:
| Logger | Env Override | Purpose |
|---|---|---|
log.SuperVisor | SCRIBE_LOG_SUPERVISOR | Startup/shutdown, orchestration, infrastructure |
log.Ingest | SCRIBE_LOG_INGEST | Transcription pipeline, event store |
log.Monitoring | SCRIBE_LOG_MONITORING | Wide-log output, observability, channels |
log.License | SCRIBE_LOG_LICENSE | License verification |
log.Config | SCRIBE_LOG_CONFIG | Configuration parsing and validation |
All loggers inherit from log.level by default. Set log.Monitoring = "off" to disable wide-log output entirely.
Migration:
# Oldlog.EventStore = "debug"log.Settings = "warn"
# Newlog.Ingest = "debug"log.Config = "warn"# New environment variablesSCRIBE_LOG_INGEST=debugSCRIBE_LOG_CONFIG=warnSCRIBE_LOG_MONITORING=off # Disable wide-log outputSee Upgrading: logging.
HTTP Configuration Restructured
BREAKING: All HTTP channels now use a unified socket model. Old monitoring and REST listener config keys are removed.
| Old Config | New Config |
|---|---|
monitoring.hostname | http.host or http.sockets.<name>.host |
monitoring.port | http.port or http.sockets.<name>.port |
SCRIBE_MONITORING_PORT | SCRIBE_HTTP_PORT or define named socket |
SCRIBE_MONITORING_HOSTNAME | SCRIBE_HTTP_HOST or define named socket |
New configuration model:
- Default socket binds to
localhost:8080(safe-by-default) - Named sockets defined in
http.sockets.*for network separation - Channels reference sockets by name:
channels.rest.socket,monitoring.socket
Migration:
# Oldmonitoring { hostname = "localhost" port = 9001}
# New (simple: everything on one port)http { port = 8080 host = "0.0.0.0"}
# New (separated: monitoring on internal port)http { port = 8080 host = "0.0.0.0" sockets.internal { port = 9001, host = "localhost" }}monitoring.socket = "internal"See HTTP Server Guide for migration details and Upgrading: HTTP sockets.
Endpoint Path Changes
BREAKING: Status endpoints have been renamed. No aliases are provided.
| Old Path | New Path |
|---|---|
/status | /observe/status |
/healthy | /observe/health or /healthz |
/ (metrics) | /metrics |
/-/* (legacy monitoring endpoints) | Removed |
Migration: Update monitoring tools, dashboards, and Kubernetes probes to use the new paths. Use /observe/health/ready or /readyz for readiness-only checks.
Note: /observe now serves interactive OpenAPI documentation for the Observe endpoints.
See Upgrading: endpoints.
Health Endpoint Format
BREAKING: Health endpoints now return MicroProfile Health JSON format.
// Old /healthy response{"healthy": true, "ready": true, "sync": {...}}
// New /observe/health/ready response{"status": "UP", "checks": [{"name": "ingest", "status": "UP"}]}Use /observe/health for combined MicroProfile health status.
Migration for Kubernetes:
# OldlivenessProbe: httpGet: path: /healthy
# NewlivenessProbe: httpGet: path: /livezreadinessProbe: httpGet: path: /readyzstartupProbe: httpGet: path: /startedzSee Upgrading: endpoints.
Metric Naming Standardization
BREAKING: All metrics now use the scribe.* prefix with dot notation. Prometheus auto-converts to snake_case (scribe_*).
Migration: Update Prometheus queries and Grafana dashboards. See Observability for the full migration mapping.
Common renames:
| Old | New |
|---|---|
directory.query.time | scribe.directory.query.time |
channel.ldap.errors | scribe.channel.ldap.errors |
hints.persistence.* | scribe.hints.persistence.* |
scribe.tasks.ratio | scribe.tasks.pressure |
Virtual Attribute Syntax
BREAKING: Virtual attribute placeholders renamed for consistency.
{{current.*}}β{{self.*}}
Migration: Update virtual attribute configurations. Startup fails with instructions if legacy syntax is found.
# Oldfilter = "(member={{current.entryDN}})"
# Newfilter = "(member={{self.entryDN}})"See Upgrading: virtual attributes.
Query Scoping Validation
BREAKING: Broad searches that could span multiple entry types must now include an explicit type constraint. Ambiguous queries are rejected early with a clear, actionable error.
Migration:
- Add a type constraint (for example
entryTypeor a type-identifying condition such asobjectClass) to any broad queries. - If you rely on wildcard/broad bases, test queries in staging and update filters/config accordingly.
Error Code Naming
BREAKING: Error codes are now standardized to SCREAMING_SNAKE_CASE. If clients match on error codes, update them to the new values.
Migration: See Failures for HTTP/REST error codes, headers, and response schema.
Support workflow: When reporting issues, include Error-Id, error.timestamp, and (if present) Trace-Id and your Correlation-Id. See βReporting Issues to Supportβ in the Failures guide.
Transcribes Configuration Syntax
BREAKING: The transcribes configuration now uses object syntax where the key is the transcribe type:
transcribes { user { ldap { base = "ou=users,o=data", filter = "(objectClass=Person)" } } group { ldap { base = "ou=groups,o=data", filter = "(objectClass=groupOfNames)" } }}Legacy array syntax still works but logs a deprecation warning. Transcribes are processed alphabetically for deterministic behavior.
Deprecations
Configuration Key Format
Configuration keys have been standardized to kebab-case. Legacy camelCase and underscore_case keys still work but log deprecation warnings.
| Old Key | New Key |
|---|---|
ldap.bindDN | ldap.bind-dn |
ldap.bindPassword | ldap.bind-password |
database.maxPoolSize | database.max-pool-size |
channels.ldap.listen.tcpNoDelay | channels.ldap.listen.tcp-no-delay |
Action: Update configuration files. Deprecated keys will be removed in a future major release. See the Configuration Reference for all current keys.
Whatβs New
Documentation Site
IdentityScribe now includes an embedded documentation site at /docs (on-prem) with /ref/* deep links into config/telemetry/error references, and publishes the same content as a public reference at scribe.identity-hub.io.
Improved 404 Handling
Unmatched HTTP routes now consistently return 404 Not Found instead of 500 Internal Server Error. A catch-all handler ensures clean 404 responses for any path that doesnβt match a registered route.
REST API Channel
A new REST/JSON channel provides HTTP access to directory data with OpenAPI documentation.
- OpenAPI 3.1.0 specification with an interactive UI (Scalar) at
/api - Collection endpoints at
/api/entries/{type}with filtering, sorting, and pagination - Single entry lookup at
/api/entries/{type}/{id}β accepts Global ID, UUID, UOID, or DN identifiers - Global ID field β entries include a Relay-style
idfield (base64url("{type}:{base36(uoid)}")) when operational attributes are selected - Multi-format filters β FleX, JSON, SCIM (RFC 7644), or LDAP (RFC 4515) syntax auto-detected
- OpenAPI content negotiation β JSON, YAML, or HTML (Scalar UI) at
/apibased onAcceptheader - JSON mapping β attribute names preserve requested alias/case; wildcard selection (
*/+) uses canonical catalog names - Parameters β list-type parameters (
fields,sort,include) accept JSON arrays/objects in addition to comma/whitespace-separated strings - Conditional requests (LOOKUP) β
ETag+If-None-Matchβ 304 for efficient polling (ETags are weak validators because representations vary byfields) - Code samples β embedded curl, JavaScript, TypeScript, and Go examples in the OpenAPI spec
# REST binds to the unified HTTP server (`http {}`).http { host = "0.0.0.0" port = 8080}
channels.rest { enabled = true # socket = "api" # Optional: bind REST to a named socket}Or via environment: SCRIBE_REST_ENABLED=true SCRIBE_HTTP_PORT=8080
See the REST Channel Guide for endpoints, parameters, and examples.
-
REST Conditional Requests: Added
Last-Modifiedresponse header andIf-Modified-Sincerequest support for timestamp-based cache validation. ETag remains the preferred validator. RFC 7232 precedence is enforced: If-None-Match takes precedence over If-Modified-Since. -
REST Cache-Control Headers: Added explicit
Cache-Controldefaults:- Lookup:
private, max-age=0, must-revalidate(enables conditional requests for authenticated/PII data) - Search:
no-store(prevents caching of streaming/parameterized results)
- Lookup:
-
REST Prefer Header (RFC 7240): Added
Prefer: handling=strict|lenientsupport for attribute validation:handling=lenient(default): Unknown attributes in fields/sort/filter are silently ignoredhandling=strict: Unknown attributes cause 400 Bad Request withINVALID_ARGUMENTcode listing all unknowns- Responses include
Preference-Appliedheader when handling is explicitly requested
-
CORS Defaults: Default
expose-headersnow includesCache-ControlandVaryso browser clients can read caching headers.
GraphQL Channel
A new GraphQL channel provides flexible query access to directory data and change history with dynamic schema generation.
- Dynamic schema generated from entry type configuration β types, connections, and query fields
- Relay Node interface β all types implement
Nodewithid: ID!; polymorphic lookup vianode(id: ID!) - Collection queries with filtering, sorting, and cursor pagination
- Connection counts β request estimated or exact totals via
count(mode: ESTIMATED|EXACT)on entry and change connections (count-only queries skip data fetch). See GraphQL connection counts. - Single entry lookup β accepts Global ID, UUID, UOID, or DN
- Change history API β global
changesfeed and per-entrychangesfield with filtering by time, type, and attributes - Temporal lookup β query entries at any point in time via the
atargument - GraphiQL UI β interactive query builder at
/graphqlwith schema explorer - Automatic Persisted Queries (APQ) β reduce bandwidth with query caching
- Query security limits β depth and complexity limits protect against DoS
Enable via environment: SCRIBE_GRAPHQL_ENABLED=true
See the GraphQL Channel Guide for query patterns and configuration.
MCP Channel (Model Context Protocol)
AI coding assistants (Cursor, Claude Desktop, VS Code, Windsurf) can now query IdentityScribe directly from your IDE.
- Data MCP (
/mcp) β Search entries, browse change history, look up schemas, and access reference docs - Observe MCP (
/observe/mcp) β Check health, diagnostics, and golden signals without leaving your editor
Enable via SCRIBE_MCP_ENABLED=true and SCRIBE_MONITORING_MCP_ENABLED=true.
See the MCP Channel Guide for setup, one-click install badges, and the full tool reference.
API Discovery
- API Catalog at
/.well-known/api-catalog(RFC 9727) lists all API channels and schema URLs - Schema endpoints β
/api.json,/api.yaml,/graphql.sdl,/graphql.jsonfor programmatic access - GraphQL error codes now include both
code(IdentityScribe-specific) andgraphqlCode(standard) for client compatibility - GraphQL performance β history queries only load requested fields (diff, patch, merge, meta) from the database
Rule-Based Log Filtering
Suppress log noise from fast/successful operations using glob patterns and LDAP-style filters:
monitoring.log.rules = [ { action = exclude, where = "(&(scribe.result=ok)(duration.seconds<=50ms))" } { action = include, where = "(duration.seconds>=5s)" }]Rules evaluate first-match-wins. Unmatched operations fall back to monitoring.log.sample-rate.
See Observability: Wide Logs for syntax and examples.
FleX Query Language
FleX is a flexible filter syntax for runtime queries and configuration filters. Supports natural prefixes (status is active), lenient whitespace, logical combinators, IN lists, attribute groups, and all SCIM operators.
See the Filters Reference for syntax and examples.
OpenAPI for Operational Endpoints (/observe)
IdentityScribe now serves interactive OpenAPI documentation for the /observe/* endpoints at /observe (Scalar UI).
- OpenAPI 3.1 spec: JSON at
/observe?format=json, YAML at/observe?format=yaml - Scalar API Reference: HTML UI at
/observe - Status JSON:
/observe/status(see Breaking Changes)
Configuration inherits from the root openapi {} section with per-endpoint overrides:
openapi { enabled = true ui { enabled = "auto" # UI: enabled in dev/test, disabled in prod theme = "default" }}
# Example override for Observemonitoring.observe.ui.theme = "purple"UI defaults are app.mode-aware (auto) for both REST and Observe. Developer tools and the test request button are also auto-gated; override via openapi.ui.* or channels.rest.ui.* (env: SCRIBE_OPENAPI_UI_*, SCRIBE_REST_UI_*).
Portal and Operator UI
Built-in web interface for operators:
- Portal (
/) β System status, golden signals, and navigation to all features - Entries (
/ui/entries) β Browse and search entries with point-in-time navigation - Changes (
/ui/changes) β Change feed with filtering and diff viewing - Observe (
/ui/observe) β Health dashboard with pressure gauges and JVM metrics
See the Monitoring Guide for all views and operational workflows.
Change History API
Two new REST endpoints provide access to change history:
GET /api/changesβ Global change feed across all entriesGET /api/entries/{type}/{id}/changesβ Change history for a specific entry
Both endpoints support time range filtering (since/until), event type filtering (type), attribute change filtering (affects), and standard pagination. Response shapes include nodes, edges, patch, merge, meta, and data options.
See the interactive API documentation at /api for full parameter details and examples.
Point-in-Time Lookup
Entry lookups now support temporal queries via the at parameter:
# Get entry state at a specific timestampGET /api/entries/users/{id}?at=2024-04-22T08:43:50Z
# Get entry state from 1 hour agoGET /api/entries/users/{id}?at=1h
# Get entry state at a specific event (using cursor from change history)GET /api/entries/users/{id}?at=AAFntW9XAAAAAAApQwAAUse with the Change History API to navigate between versions.
Entry Verification Timestamp
Added verifiedTimestamp for tracking when an entry was last verified during reconciliation.
- Accepted names:
lastVerifiedAt,lastSeenVerified,verified_at,verifiedAt,verifiedTimestamp - LDAP name:
verifiedTimestamp(matchescreateTimestamp/modifyTimestamp) - REST JSON: preserves the requested alias/case for attribute keys
- Type: ISO-8601 in REST, GeneralizedTime in LDAP
- Delegation: not delegated to upstream LDAP backends
Unified HTTP Server
All HTTP traffic now runs through a single WebServer with named sockets.
- AIMD-based concurrency limiting with auto-derived limits from global
concurrencysetting - Request limits β
max-payload-sizeandmax-in-memory-entityper socket - Connection limits β
max-tcp-connectionsandmax-concurrent-requestsper socket - CORS support β per-socket CORS with magic origins (
same-origin,host,origin,*) and explicit origin lists - Gzip compression enabled by default
- Graceful shutdown with configurable drain period
- HTTP/2 support via
SCRIBE_HTTP2_ENABLED - PII-safe telemetry defaults β common sensitive headers and client addresses are redacted in HTTP telemetry/logs by default
http { request-limits { max-payload-size = 10MiB # or unset for unlimited max-in-memory-entity = 128KiB } connection-limits { max-tcp-connections = 1000 # or unset for unlimited max-concurrent-requests = 500 } cors { enabled = true allow-origins = ["same-origin"] # or explicit: ["https://app.example.com"] allow-credentials = false }}See the HTTP Configuration Reference for the http {} configuration block.
OpenTelemetry-Native Observability
Migrated from Micrometer to OpenTelemetry SDK.
- Prometheus at
/metrics(OTel-backed) - OTLP export for traces and metrics to collectors like Grafana Alloy
- Histogram exemplars link metrics to traces in Grafana
- Wide event logs β one structured log per operation with configurable sampling
- Pressure metrics β single-number health indicators (
<1 = healthy, β₯1 = contention)
monitoring { traces.enabled = true exemplars = "traceBased"}See the Monitoring Guide for setup and Observability for metrics inventory.
Monitoring Bundle
IdentityScribe now ships a ready-to-run monitoring bundle (Grafana dashboards, Docker Compose, and a Helm chart). Itβs included in the distribution under monitoring/.
See the Monitoring Guide for setup and deployment options.
MicroProfile Health Endpoints
Health checks now follow MicroProfile Health 4.0 with Kubernetes-friendly aliases.
| Endpoint | Purpose |
|---|---|
/readyz | Readiness (plain text) |
/livez | Liveness (plain text) |
/startedz | Startup (plain text) |
/healthz | Combined health (plain text) |
/observe/health | Combined health (JSON) |
/observe/health/ready | Readiness (JSON) |
/observe/health/live | Liveness (JSON) |
/observe/health/started | Startup (JSON) |
/observe/health/check/{name} | Single health check (JSON) |
Operational Endpoints
New endpoints for operational visibility:
| Endpoint | Purpose |
|---|---|
/observe/status | Basic status with uptime |
/observe/doctor | Health report with threshold checks and recommendations |
/observe/pressure | Pressure metrics with actionable hints |
/observe/services | Service lifecycle status and uptime |
/observe/indexes | Index build status and concurrent build detection |
/observe/channels | Enabled channels, sockets, and runtime binding info |
/observe/config | Resolved configuration (passwords redacted) |
/observe/license | License status and renewal request |
/observe/hints | Persisted hints and recommendations |
/observe/signatures | Query signature inventory |
/observe/stats/values | Value size statistics per entry type and attribute |
/observe/stats/entries | Entry blob size percentiles per entry type |
/observe/stats/events | Event rate windows (dashboard-friendly buckets) |
/observe/stats/ingest | Ingest lag and checkpoint positions |
Readiness probes now incorporate index build status; use /observe/indexes to diagnose readiness delays.
Notes:
/observe/hintsis available only when hint persistence is enabled./observe/signaturesrequires EXPLAIN sampling to be enabled./observe/doctorand/observe/pressurerequire metrics recording to be enabled.
Connection Hints
Tune PostgreSQL session parameters per-channel for different workload profiles.
database.connection-hints { statement-timeout = 60s work-mem = "256MB"}
channels.rest.connection-hints { statement-timeout = 30s # Override for REST only}Environment variables: SCRIBE_REST_STATEMENT_TIMEOUT, SCRIBE_LDAP_WORK_MEM, etc.
Virtual Attribute Delegation
Virtual attributes can now be forwarded to upstream LDAP backends when they support the attribute.
ldap.virtualAttributes { memberOf { filter = "(&(entryType=group)(member={{self.entryDN}}))" value = "{{other.entryDN}}" delegatable = true # Forward to backend } securityEquals { # ... delegatable = "equivalentToMe" # Rewrite name when delegating }}Virtual Attribute Type Scoping
Virtual attributes can now be scoped to specific entry types via the types field:
ldap.virtual-attributes { memberOf { types = ["user"] # Only expose on user type filter = "(&(entryType=group)(member={{self.entryDN}}))" value = "{{other.entryDN}}" }}Omit types to expose on all types (backward compatible). Transcribe-level VAs are automatically scoped to their transcribeβs type. REST strict mode (Prefer: handling=strict) and GraphQL schema validation enforce type scoping at runtime.
Note: Transcribe-level VAs are now scoped to their defining transcribe by default. See the LDAP Configuration Reference for details.
Query Planner v2
The SQL generation engine has been rewritten for better PostgreSQL performance.
- Smart index utilization with truncation-aware patterns for long text values
- Stable query plans through padded IN-lists (up to 8 values) and array operators for larger sets
- Virtual attribute support including nested cross-references
- Cursor-based pagination with efficient forward/backward paging
Backpressure Under Query Load
Under high query load, IdentityScribe applies backpressure to protect the database:
- HTTP channels (REST/GraphQL) fail fast with
503 UNAVAILABLEandRetry-Afterwhen the system is busy - LDAP blocks up to the configured time limit (rather than failing fast)
Tune via database.query-http-acquisition-timeout (see Database Configuration).
Transcription Throughput Tuning
Transcription uses a bounded worker pool with a bounded queue to reduce memory pressure under load.
- Configure per-transcribe throughput with
transcription.workersandtranscription.queue-capacity(defaults are{auto}) - See the Transcribes Configuration Reference for tuning guidance and defaults
Sortable Index Configuration
Attributes listed in indices.sortable now receive sortable indexes even if they are single-valued, improving ORDER BY performance.
If you previously relied on automatic filtering of single-valued attributes, re-check your index configuration (see the Configuration Reference).
Platform Support (PostgreSQL + Toolchain)
-
PostgreSQL: 18 is the primary target, 17 is fully supported, and 15/16 are deprecated compatibility targets
-
Native binary: the distributed artifact is a native executable (no Java runtime required to run)
-
Building from source: the build toolchain targets Java 25 / GraalVM 25
-
50-60% faster startup through parallel service initialization
-
Non-blocking index builds via
CREATE INDEX CONCURRENTLY
Logging and Operator Experience
Improved startup banner, log formatting, and sampling behavior.
Startup banner now shows mode, license, config sources, backend URLs, channel bindings, and portal URLs. Printed after services are running, so port bindings are accurate:
identity-scribe v1.2.3 ready in 847ms
β Mode productionβ License ACME Corp β expires 2027-01-15 (365d)β Readonly true
β Configβ /etc/scribe/identity-scribe.confβ environment variables
β Database postgresql://db.example.com:5432/scribe (ssl)β LDAP ldaps://dc01.example.com:636
β Channelsβ REST API http://localhost:8080/apiβ LDAP ldap://localhost:10389Other improvements:
- PRETTY format uses ANSI colors for result status (green/yellow/red) and attribute prefixes. Auto-detected; disable with
NO_COLOR=1. - Exception bypass β operations with exceptions now bypass log sampling (same as warnings)
- Third-party loggers β HikariCP, Flyway, and others are configurable via
SCRIBE_LOG_*env vars
See the Configuration Guide for log format options.
Failure Handling and Support
The Failures guide includes a βReporting Issues to Supportβ checklist, including which IDs/headers to include (Error-Id, Trace-Id, Correlation-Id).
Log Noise Reduction
Reduced log verbosity during initial sync and routine operations:
- Default noise gates β
Hints.*andMetrics.*suppressed unless slow or failing - Queue wait threshold β
monitoring.log.queue-wait-threshold(default: 90s) controls when warnings emit - Segment leak suppression β automatically suppressed during initial sync
- LDAP search events β short-lived segments instead of long-lived parent, eliminating leak warnings
Fixes
- Graceful shutdown β HikariPool connections are now soft-evicted before pool closure, eliminating timeout warnings
- Timezone handling β Fixed timestamp drift in non-UTC JVM timezones; JDBC connections now set PostgreSQL session timezone to UTC
- LDAP virtual attribute delegation β Virtual attributes are forwarded correctly during backend delegation instead of being rejected
- Virtual attribute warnings β Fixed a false warning for self-referencing virtual attributes; the
entryTypewarning now only triggers for cross-referencing templates - Pagination metadata β Cursor pagination now correctly indicates whether more results exist before/after the current page
- Backward paging β Backward navigation streams efficiently without buffering entire pages
- Time limit enforcement β Search time limits now cancel database work promptly
- Query cancellation β Failed or timed-out requests cancel remaining database work immediately