All versions since v3.0.0-rc.7 (2026-06-12)
v3.0.0-rc.7 (2026-06-12)
Breaking Changes
- Observe endpoints require authentication by default — Anonymous requests to
/observe/doctor,/observe/channels, and other Observe JSON endpoints now return401unless an operator session or accepted credential is present. Migration: For trusted local evaluation, setmonitoring.observe.auth.enabled=falseorSCRIBE_OBSERVE_AUTH_ENABLED=false. For shared environments, sign in or send an approved credential. - Browser sign-in sessions require production keys — Production deployments that enable browser sign-in now fail startup until session-cookie keys are explicitly configured. Migration: Configure session-cookie keys before enabling browser sign-in in production; development setups keep local convenience behavior.
- GraphQL schema and introspection require authentication when anonymous access is disabled — Requests for schema SDL, introspection JSON, and helper routes (
/graphql.json,/graphql.sdl,/graphql/schema.*) now match query execution access rules. Migration: Send an approved credential to schema endpoints, or enable anonymous access. - Explicit distinguished-name binds are confirmed by the directory — LDAP binds with an explicit
dn:value are now accepted only when the directory confirms that distinguished name. Other authentication methods can no longer authorize a caller-supplied DN they cannot prove. Migration: Use username binds or an authentication method that can prove the DN. - Insecure authentication-provider TLS is development-only — Production deployments that set
auth.http.ssl.insecure=truenow fail startup. Migration: Configureauth.http.ssl.trust-storewith the provider CA, remove the insecure setting, or keepauth.http.ssl.insecure=trueonly for local development. - Credentialed CORS cannot reflect every origin — HTTP configurations that combine
allow-credentials=truewith reflected origins now fail startup because they would grant credentialed browser access to any requesting origin. Migration: List trusted origins explicitly, or turn credentials off for reflected-origin development setups.
Added
- Query diagnostic reports include corpus context — Reports now show directory scale, field coverage, statistics freshness, and replay guidance. When context is unavailable, the report and export call out the missing data directly.
- Change history gains hot-entry ranking across UI and APIs — The Operator UI, REST (
/api/changes/hot), GraphQL (hotChanges), and MCP (hotChanges) now rank the most frequently changed entries in a time window, with the same filters and cursor paging as the change feed. - GraphQL and MCP lookups can compare entry states over time — Integrations can now request one entry at two points in time through GraphQL or MCP. GraphQL returns the before and after entry states, while MCP lookup responses include before and after details plus a JSON Patch summary for the selected attributes.
- Observe highlights slow operator workloads — Observe Inbox and Queries now surface when entry searches, lookups, change history, hot-entry rankings, and entry comparisons are slow or recurring, with grouped cards, timing summaries, and next steps for each pattern. Admin operators see a timing chip on entry search, lookup, change, and comparison views that links to the matching Observe workload detail. Support can download a redacted workload report without exposing request identities or filter values.
- MCP change-history tools match REST and GraphQL — The MCP
changestool acceptssince/untiltime bounds,range=allfor unbounded history, andbeforefor backward pagination. See MCP Channel. - Startup warns when legacy configuration keys are ignored — v2 configuration paths that are present but no longer read are now reported at startup so upgrades do not silently keep obsolete settings.
- Directory servers with legacy RSA cipher suites can be reached with an explicit opt-in — TLS_RSA cipher suites are disabled by default because they lack forward secrecy. Listing the required suite in the LDAP connection’s
ssl.cipher-suitessetting re-enables it at startup (must be literal values, not environment-variable substitution). IdentityScribe logs a warning while the opt-in is active; prefer enabling modern (ECDHE) cipher suites on the directory server.
Changed
- API reference pages use newer embedded explorers — REST, Observe, and GraphQL documentation pages ship with refreshed bundled assets for better schema browsing, search, and local request trials. Same-origin Try Request calls keep browser sign-in credentials, while offline deployments continue to use bundled assets.
- Change history opens with a broader recent window — The Operator UI and generated API examples now start with the last 24 hours instead of one hour, making recent activity easier to discover in low-change directories. API clients can still request one hour,
range=allwith paging, or any supported time range explicitly. - Entry comparison is clearer and shareable — In the Operator UI, compare mode keeps time range and event filters with the event timeline they affect, while the comparison keeps its before and after controls in the diff. When a selected range does not include the compared points, the timeline dims the outside-range area. Operators can choose before and after points from a change timeline, a compact change-density view, or explicit timestamps. The selected comparison stays in the URL so the view can be shared.
- Observe UI is easier to scan and act on — The Services view focuses on status and action items first, with LDAP forwarding details on their own tab including forwarding rates, reason breakdowns, and guidance for strict-isolation setups. Recommendation detail pages now summarize linked evidence and show next steps, related views, and verification guidance. The configuration reference explains default protection on Observe endpoints and which settings control write actions.
- Observe evidence pages are easier to compare and share — Workload evidence, storage coverage, Inbox recommendation detail, and Query Diagnostic Report pages now follow a consistent evidence-first layout with action summaries, related links, and support export controls where available. Diagnostic reports keep timing and captured request samples together, while lower-level request details stay in a collapsed appendix. Dates, durations, counts, byte sizes, percentages, and rates use the same display rules across rows, detail panels, reports, and exports.
- Relationship modeling and deployment guidance cover common production choices — Attribute modeling guidance explains when to store relationship data as regular attributes versus virtual attributes, with group, role, manager, and owner examples. The deployment guide now covers single-writer topology (one non-readonly instance for transcription and maintenance, with optional read-only replicas for query channels). See Attribute Modeling and Deployment.
- Wide operational logs omit raw URL query strings by default — HTTP error diagnostics no longer write full query strings into wide logs unless an operator configures log redaction otherwise. Trace export already omitted this field; wide logs now match that default.
- REST searches have a default server-side deadline — REST searches now use a 30-second server-side deadline when no request-specific search limit is configured, matching GraphQL behavior and preventing slow searches from running indefinitely.
- Single-entry lookups are faster in large directories — REST, GraphQL, and LDAP requests that ask for one exact identity now avoid unnecessary broader search work. Exact LDAP base-DN lookups were about 40× faster in benchmarks; typed REST/GraphQL lookups by DN, UUID, numeric ID, or not-found result were about 10–25× faster. Selected attributes, access checks, errors, and broader searches keep their existing behavior.
- Change history and entry comparison requests are faster in large directories — Change history responses that do not request event details now avoid extra work, and entry comparisons return faster when comparing two points in time.
- High-entropy values are stored without wasted compression effort — Attribute values that are already random or compressed (certificates, encrypted or binary blobs) now skip the compression step, lowering write-time work on incompressible data. Values read back are unchanged; existing stored entries are unaffected.
- Large sorted pages have a higher safety ceiling — The default continuation safety limit now leaves more headroom for very large directories, so eligible sorted pages can finish without falling back too early. If you override
database.continuation.window.absolute-scan-cap, review that value against the new default before upgrading.
Fixed
-
Paginated searches no longer omit, duplicate, or misorder edge-case results — The fixes cover:
- Sorted searches that filter on one attribute and sort on another, as well as unfiltered sorted searches, now include entries that match the filter but have no value for the sort attribute. Entries with no sort value are placed after all valued entries for ascending sort and before them for descending sort, matching RFC 2891. Paging through no-value entries stays fast on large directories.
- OR-filter searches now deduplicate overlapping matches and keep entries without a sort value in page cursors.
- Searches sorted by entry name now use consistent continuation values, so moving through pages does not skip or duplicate rows when full DNs and short entry names sort differently.
- Virtual-attribute searches (such as group membership) now avoid unnecessary directory-wide work while preserving matches, ordering, and pagination.
- Large sorted searches that combine filters, prefix matching, cursor paging, or virtual attributes now avoid unnecessary broad work more often while preserving matches, ordering, and pagination on large directories.
- Searches that combine an exact match with additional conditions now apply all of them instead of returning broader results.
- Requests that include an empty alternative inside a larger filter now preserve the client’s intended match behavior.
- Filters on mixed-case attribute names (uppercase letters) now match and sort correctly.
- Query diagnostic reports now explain when sorted searches with OR alternatives cannot use the faster optimization.
- During startup, upgrade, or maintenance windows, sorted searches over multi-valued attributes wait for prepared acceleration data before using it, preserving correctness.
-
The Operator UI keeps state and formats values consistently —
- Opening
/uinow loads the Operator UI reliably in packaged deployments instead of showing a missing-page fallback. - Sign-in recovery works for expired and missing sessions: navigation and background refreshes show a sign-in prompt that returns operators to the same page after login; opening a protected page without a session redirects directly to sign-in.
- The browser sign-in screen now follows the selected Auto, Dark, or Light color mode instead of always rendering in dark mode.
- Recommendation times stay stable when browser clocks differ: future-dated observations from the server are treated as current local observations, so negative “ago” labels no longer appear.
- Observe refreshes show partial data honestly: if one panel fails, the UI marks data as partial, clears stale health details, and points operators at the affected area.
- Entry history and change views keep a recent time range selected, preserve that range while moving through timeline entries, and open entry links through the app router instead of briefly changing and reverting the page.
- Modals and panels close with Esc or an outside click. The attribute change sparkline keeps its most recent point. Database Health and Coverage panels ignore superseded background refreshes. The Observe view no longer persists raw diagnostic text. Free-text
~=searches load back into the query builder. Entry comparisons stay distinct when two points share a timestamp. Observe scope filtering matches regardless of letter case. - Light mode no longer looks disabled: cards, tables, status chips, and recommendation rows use clearer contrast.
- Multi-value diffs read consistently: an attribute side with no removed or added values no longer shows a contradictory “(no changes)” label, and before and after value lists now page together.
- Comparison markers on the change-density timeline keep their position while events load and when the time range changes. The timeline axis matches the selected range.
- Dense bursts in entry history and attribute change indicators now expand across their local time span instead of appearing as a single bar.
- Header copy buttons now report success or failure and use a fallback when direct clipboard access is unavailable.
- The UI recovers after a new deployment: when a browser has an outdated app open, the view surfaces an error with a reload option instead of showing a blank view. Entry list links encode the entry type so types with reserved URL characters open correctly.
- Observe Inbox badges now count grouped recommendations that still need review, so dashboard and sidebar totals match the Inbox list.
- Point-in-time links restored from change cursors now open the intended historical entry view. Change-history toolbar controls keep a stable layout on narrower screens.
- Opening
-
Change history fixes —
- Previous-page navigation works correctly: moving back requests entries before the current cursor instead of showing the next page again.
- Affected-attribute filters handle punctuation safely: quotes, semicolons, and other punctuation are treated as ordinary text.
- REST change-history filters accept the same singular and plural entry-type names as other directory requests.
- Search cursors keep working when clients rely on the default page size.
- Copied entry links keep special characters intact.
- URL-restored Operator UI filters no longer rewrite active searches.
- Per-type change URLs such as
/ui/entries/users/changesnow redirect to the global Changes view with the entry-type filter applied. - Hot-entry ranking requests now honor deadlines and disconnects like other change-history requests.
-
Startup and operations —
- Startup after an interrupted initial sync no longer skips entries: IdentityScribe detects the incomplete state and performs a full re-enumeration. Affected entries are reprocessed and brought up to date. This also resolves a startup hang where a restarted sync worker could wait indefinitely for a startup gate already released in a previous run.
- Directory synchronization repairs stale ownership conflicts automatically: when an entry moves in the directory while IdentityScribe is down or retrying, synchronization re-checks the directory, releases stale local ownership, and retries the affected entry without advancing past unresolved work. Readiness stays unavailable while repair is pending and resumes after the database has converged. Monitoring now reports repair-pending and repair-completed states separately, and only unresolved repair failures are logged as errors.
- Transient directory errors during startup are retried automatically (up to twice) before startup is treated as failed.
- Threshold-gated maintenance jobs no longer run when every configured threshold signal is missing. IdentityScribe warns about the missing signal so operators can fix the setting instead of unknowingly bypassing the gate.
- Startup and monitoring logs are quieter during healthy operation: successful LDAP startup checks and database statistics refreshes no longer emit routine informational lines. Failures and warnings still appear.
- Intentional shutdown during startup is treated as a clean cancellation instead of a spurious failed start.
- Startup fails fast when a service fails during parallel startup: the failure surfaces promptly instead of waiting on the still-starting service and appearing to hang.
- Invalid HTTP socket protocol settings fail startup clearly instead of silently falling back to an unintended listener shape.
- Stalled initial synchronization is visible in monitoring: when a transcribe’s baseline has been restored but its initial scan is incomplete and the sync worker is stuck in a restart loop, the
scribe_ingest_sync_stalledgauge increments and theingest.sync_stalleddoctor check turns degraded. The readiness snapshot reason changes fromcoherent-restored-baselinetorestored-baseline-sync-incomplete. Setmonitoring.ingest.fail-readyz-on-failed-state=trueto also take the transcribe out of the readiness gate when its sync worker enters a terminal-failed state. - Ingest health now reports initial completion separately from current lag, so complete-but-lagging transcribes are not mistaken for incomplete startup work.
- Successful write batches still commit when statement cleanup reports an error after execution, preventing completed persistence work from being rolled back by cleanup failures.
- Fatal maintenance errors now surface to service failure handling instead of being lost during scheduled cleanup.
- Native-image deployments keep embedded documentation and UI assets available across repeated requests.
-
Security and redaction —
- Authentication logs are safe and useful for access debugging: wide logs show bounded details such as failure reason, method, issuer, and key id. Subject identifiers, usernames, and client addresses use marked short hashes. Passwords, bearer tokens, cookies, and Authorization headers remain always replaced. Wide logs also note when a single-valued attribute was truncated on read.
- JWT and token-introspection authentication accept configured public issuer URLs while still using the configured provider endpoint for discovery, so deployments with separate internal and public issuer addresses validate tokens consistently.
- Browser session cookies choose the secure-cookie variant from the trusted request security state instead of raw forwarded-protocol headers.
- Exported traces apply redaction consistently: linked trace metadata and unlisted numeric/boolean attributes are now covered, so configured identity and credential protections are applied before data leaves IdentityScribe.
- MCP directory tool responses render returned values safely: text is wrapped in a safe plain-text form before presenting as Markdown, so directory values cannot appear as commands, headings, or links.
- MCP lookup errors escape invalid identifiers before rendering Markdown, so rejected input cannot appear as headings, links, or code.
- Invalid API requests now name the rejected parameter and why. The Operator UI shows that detail in search and change views and clears stale search cursors.
- Unauthenticated browser asset access is limited to known static asset prefixes; application routes that contain
/-/now require authentication as configured. - LDAP proxy-authorization controls are rejected by local LDAP handling instead of trusting a caller-supplied delegated identity.
- Hyphenated API-key attributes are now covered by default telemetry redaction.
-
Channel APIs return consistent errors, metadata, and paging behavior —
- REST requests with blank optional parameters (
fields,range,since,until) now use the documented defaults. - Single-entry REST lookups report service failures correctly: if IdentityScribe cannot load an entry after the request has been validated, the response reports a service failure with clearer text instead of a conflict response.
- Point-in-time entry lookups accept the same identifiers (DN, UUID, numeric ID) as current lookups. Numeric-ID lookups can also return deleted historical entries, including response validators, instead of missing the reconstructed entry.
- GraphQL type names no longer collide with built-in scalars.
- GraphQL typed lookups return
nullfor malformed identifiers instead of surfacing resolver failures. - GraphQL schema helper requests follow the configured schema access rules, and hot-entry fields avoid naming collisions with configured entry types.
- Generated REST API descriptions remain valid for punctuation-heavy type names.
- Generated REST API descriptions now document lookup comparison responses accurately.
- REST and GraphQL searches honor cancellation, deadlines, and disconnects: they stop promptly and report cancellation or timeout instead of returning late results or a 500 error.
- Typed entry lookups accept short distinguished names: single-RDN identifiers such as
uid=jdoeresolve under the requested entry type’s configured base. Genericnode(id:)lookups and plain names keep existing behavior. - LDAP search controls meant for the backend directory are now forwarded instead of dropped.
- LDAP Virtual List View requests with extreme before or after counts are bounded safely instead of overflowing.
- LDAP type-detection searches preserve the attribute spelling clients depend on:
objectClassand similar filters keep that spelling in local responses when clients use default attributes or*selection. Explicitly requested spellings still take precedence; matching remains case-insensitive. - LDAP responses preserve canonical attribute names for synchronization metadata while keeping client-requested casing for explicitly requested attributes.
- LDAP product fingerprints are more stable: detection no longer changes its fingerprint only because a directory serves the same UUID attribute in a different value form.
- Forwarded LDAP searches no longer look like setup failures: they are recorded with bounded informational details instead of repeated warnings, keeping logs focused on real failures.
- Stored entries are checked for truncation and malformed content on read: incomplete records are rejected instead of returning partial data.
- MCP change-history responses escape missing identifiers safely, and MCP reference lookups remain available when the embedded reference index is unavailable.
- Time-range inputs that are too large to represent now return bounded validation errors instead of unexpected server failures.
- REST requests with blank optional parameters (
-
Directory and reconciliation —
- Reconciliation reports missing-entry deletes more reliably: it waits only for the deletes it just scheduled, and repeated delete batches that exceed the completion timeout are visible in telemetry.
- Reconciliation telemetry distinguishes entry types so operators can tell which transcribe a stuck or slow pass affects.
- Reconciliation verifies fallback matches before scheduling missing-entry deletes, avoiding deletes based on an unrelated distinguished name.
- Health checks report unhealthy during automatic restart backoff so load balancers stop sending traffic until recovery.
- MCP directory tools honor client cancellation: search, lookup, and change-history tools stop running when the client cancels.
- Range-filter searches keep their lower and upper bound behavior in large sorted result sets. Explicit entry-type filters keep the requested type scope for current and point-in-time lookups.
-
Recommendations and Observe —
- Recommendation configuration examples are copy-paste safe: entry-type comments now appear after the quoted attribute value instead of inside it.
- Slow-query recommendations open the right diagnostic report: Observe matches slow-query rows to the recorded directory query even when the database truncates or normalizes the displayed text. The match is retained across restarts after a query shape has been observed once.
- Inbox cards focus on slow directory searches that operators can act on and ignore routine maintenance noise.
- Observe Inbox and recommendation detail views stay responsive when many observations support the same fix.
- Pending recommendation evidence is saved during shutdown, so recent observations remain available after restart.
- Shutdown waits for in-flight directory ownership repairs before closing, keeping repair state consistent across restarts.
v3.0.0-rc.8 (2026-06-22)
Breaking Changes
- Subtree searches that don’t name an entry type are answered locally — A directory search across a base that spans more than one configured context, without naming an entry type (for example a virtual-list-view browse from a directory client), is now answered from IdentityScribe’s own data instead of being forwarded to the upstream directory. Searches that already name a type, or target a single configured context, keep their current behavior. Migration: To keep forwarding these searches to the upstream directory, set
channels.ldap.untyped-search = delegate(orSCRIBE_LDAP_UNTYPED_SEARCH=delegate). Mark any subtree that must be served by the upstream directory as anignore-contextsentry. - Search diagnostics use one consistent model — Observe, traces, and support reports now describe search outcomes consistently, and every search strategy contributes timing evidence. Migration: Replace dashboards or alerts that depend on older internal series or labels with the supported query-latency signals in the Telemetry Reference and use Observe reports for per-search diagnostic detail.
- The Observe MCP endpoint requires authentication by default — Requests to the Observe MCP endpoint (
/observe/mcp) now require an operator session or accepted credential; anonymous requests are denied. This endpoint previously inherited the global monitoring auth setting, which left it open whenever global authentication was off. Migration: For trusted local evaluation, setmonitoring.mcp.auth.enabled=falseorSCRIBE_MONITORING_MCP_AUTH_ENABLED=false. For shared environments, sign in or send an approved credential.
Added
- Observe surfaces search-preparation cost — The Observe Inbox and query diagnostic report now show how much preparation work ran before a search and whether database connections still need per-request setup. Values read “evidence pending” until the first sample arrives, and trace export includes the same preparation timing. See Signals.
- Clearer LDAP sign-in diagnostics — When an LDAP sign-in succeeds but the directory service account cannot read the account back — an access-rights or filter configuration issue rather than a wrong password — operators now get a distinct signal that separates the two, and sign-in failures appear on the request timeline. The response sent to the client is unchanged.
- Every search records which strategy ran, and how fast each strategy is — Each query’s trace now carries the search strategy it took, so one trace shows which kind of search ran. Per-strategy latency is now recorded for every routed search, including merge and range searches that previously went unmeasured, and the Observe evidence-health panel shows per-strategy p95 latency and a recent (5-minute) mix beside the cumulative totals — so a slowdown or shift in one kind of search shows up without waiting for process-lifetime aggregates. A query’s diagnostic report now names its search strategy too, and Observe workload cards group by strategy — so two slow searches of different kinds no longer collapse into one card.
- Failure detail in logs can be redacted for shared log backends — Failure messages, failure details, and exception stack traces written to the structured and application logs can now be redacted before they leave the process, so query filter text, distinguished names, and attribute values that appear in error detail need not reach a shared SIEM. Redaction is opt-in and off by default — local incident logs keep full detail — and is set per field under
monitoring.log.redaction(exception.message,exception.stacktrace,failure.details,exception.cause.*,warning.details), using the same hash and omit options as trace export. With no redaction rule configured, log output is unchanged.
Changed
- Sorted searches that narrow to a small set of entries return quickly over large directories — A sorted or virtual-list-view browse that narrows to a small set — by entry distinguished names (a single one or a long list), by a unique identifier (
uuidoruoid), or by a selective attribute value such as a rare surname — now returns in a fraction of a second across very large directories, where the same search could previously take many seconds. This applies when the search names the entry types it spans (or its base maps to specific types) and sorts on an attribute the directory keeps sorted, on the first page and when paging forward by cursor or page offset. Results and ordering are unchanged. - Broad sorted browses served locally return quickly over large directories — A sorted or virtual-list-view browse over a base that spans several entry types, with no narrowing filter, now returns in a fraction of a second across very large directories — on its first page, when paging forward by cursor, and at a deep page offset — where the same browse could previously take tens of seconds. This applies when IdentityScribe answers the browse from its own data rather than forwarding it (see the subtree-search entry above) and the spanned types keep the sort attribute indexed. Results and ordering are unchanged.
- Observe presents evidence consistently across its views — The Observe Inbox, Workloads, recurring-workload groups, query diagnostic report, and storage-coverage views now share one evidence model, so a given signal reads the same way in every view. Expandable rows respond to the keyboard like native controls, and the views render correctly on small and mobile screens. The signals shown are unchanged.
Fixed
- Operator views label searches by strategy — The Observe planner evidence-health panel now labels each search by an outcome-framed search strategy (Filtered search, Sorted listing, Combined-condition search, Membership search, Range search), matching the per-query diagnostic report. The information shown is otherwise unchanged.
- Failed sign-ins return at a constant time — A failed credential check now returns at the same fixed time whether the username does not exist, the password is wrong, or the password was correct but the account could not be read back. Response timing no longer reveals which of these happened, so it cannot be used to confirm a valid password or probe which accounts exist. This covers credential validation at the LDAP bind, the HTTP
401response, and the browser login form; the response a client sees is otherwise unchanged. The fixed time isauth.failure-delay(SCRIBE_AUTH_FAILURE_DELAY), default 2 seconds — set it comfortably above your slowest failed sign-in for the guarantee to hold (the 2s default covers a healthy directory). Setting it to0disables both the delay and the constant-time response. - Sorted prefix searches keep accented matches — Sorted and virtual-list-view searches that filter the sorted attribute with “starts with” now keep entries whose names contain accents or other non-ASCII characters. These searches no longer omit valid matches while paging, and result ordering is unchanged.
- Sorted and paged directory searches no longer fail with an error — An LDAP search that has to be forwarded to the upstream directory (for example a sorted or virtual-list-view browse from a directory client) could return an “invalid request” error instead of the intended “this search cannot be served here” response. These searches now return the correct response.
- Directory searches that select entries by distinguished name no longer need an entry type — A subtree search that filters on one or more entry DNs (for example an Identity Hub lookup that names users and roles by their distinguished names) across a base that spans more than one configured context previously returned an error asking for an entry type. IdentityScribe now recognizes the entry type from each distinguished name and returns the matching entries. Searches whose DNs fall outside the configured contexts keep their existing behavior.
- REST error responses no longer expose internal diagnostic details — Error responses from the REST API previously included a
detailsobject that could carry internal directory layout (the configured base DNs for each entry type) and internal attribute names. REST error responses now omit that object, returning only the safe code, message, and HTTP status — matching the GraphQL and MCP channels. The code, message, and status are unchanged.
v3.0.0-rc.9 (2026-06-22)
Fixed
- LDAP discovery stays license-compatible across harmless directory metadata differences — Startup license checks no longer fail only because the connected directory exposes an equivalent identity dataset with a discovery-only distinguished-name metadata difference. Directory-specific UUID value forms still affect the fingerprint when they change how entries are identified.
- LDAP paged-search counts no longer understate complete result sets — Simple paged LDAP responses now report an exact size only when IdentityScribe knows the complete result count. Clients that page through a result set, including Identity Hub, no longer see a final response that suggests fewer available entries than were actually returned.
- Documentation pages render wider content and tables correctly — The published documentation now gives desktop pages more reading space and renders tables reliably.
- Redacted Observe support exports work for signed-in operators — Operators can now prepare redacted Query Diagnostic and Workload support exports without extra Observe write access. Full exports remain limited to operators with write access, and the Operator UI no longer shows the Full option to operators who cannot use it.
- Entry count toggle explains exact-count fallbacks — When an exact entry count takes too long, the Operator UI now keeps the exact-count toggle selected, shows that the displayed number is still an estimate, and labels the timeout instead of appearing to ignore the toggle.
- Forwarded LDAP searches can use entry distinguished-name filters — LDAP searches forwarded to the connected directory can now use
entryDN(or Active Directory’sdistinguishedName) when filtering, sorting, requesting attributes, or evaluating assertions. Identity Hub lookups by entry DN no longer fail with “Request cannot be delegated to backend server” when IdentityScribe forwards the search. - LDAP bind failures stay attached to the bind log — Failed LDAP binds that already run inside an LDAP bind operation no longer also emit a separate “Untraced error” line. Operators still get the same bind events and client response; the failure is now recorded with the operation log.
v3.0.0-rc.10 (2026-06-25)
Added
- eDirectory Identity Manager role and group setups have a ready-to-import profile — Deployments that track role and group membership on user entries can now include
edirectory-idm.confafter their base config. The profile adds the IDM role, resource, and group attributes IdentityHub commonly queries, keeps high-fanout reverse membership views virtual, and documents the expected user, role, and group mappings in eDirectory Identity Manager Roles. - Query defaults can be set once and overridden per channel —
query.default-count,query.default-limit,query.default-include,query.default-fields, andquery.default-sortnow provide shared defaults for integrations that do not set their own values. Named channel overrides such aschannels.ldap.default-count,channels.rest.default-fields,channels.graphql.default-sort, andchannels.mcp.default-limitstill let each integration choose its own behavior. - Recent forwarded LDAP searches are visible in Observe, with a value-free request shape on every trace — When IdentityScribe forwards a directory search to the connected directory, its trace and wide log now describe the request without exposing directory data: the search scope, a structural filter pattern with the values stripped (for example
objectclass?,uid=), stable digests of the base DN and filter, the requested controls, and a correlation id — underldap.request.*andldap.delegation.sample_id. This makes it possible to identify which forwarded search returned a “service unavailable” (or any other result) and trace it back to the responsible client call. The Operator UI’s LDAP Forwarding view adds an expandable list of recent forwarded searches for each forwarding reason, and the same samples are available at/observe/ldap/delegationwith a downloadable export. The literal base DN and filter are held in a bounded in-memory window and shown only to operators with Observe write access; capture is on by default and can be turned off withmonitoring.observe.capture-request-values = false. The window keeps the most recentmonitoring.observe.max-samplessearches (default 200) formonitoring.observe.ttl(default 15 minutes) and is never written to the database. Traces, metrics, and the redacted export never carry the literal base DN or filter. - MCP endpoints record tool usage, expose per-tool metrics, and limit concurrent tool calls — The data (
/mcp) and Observe (/observe/mcp) MCP endpoints now record one audit log line per tool invocation (endpoint, tool name, result, duration, and request id — never the tool arguments), and export per-tool request-count and latency metrics underscribe.mcp.tool.*. Both endpoints bound how many tool calls may run at once: the cap auto-derives from the configuredconcurrencysetting (the same knob that sizes the database pool and HTTP limits) when unset, and can be set explicitly viachannels.mcp.max-concurrent-tools/monitoring.mcp.max-concurrent-tools(0disables the limit). When the limit is reached, an incoming call waits for a free slot before being rejected with a clear error; the wait defaults to 1 second and is tunable viachannels.mcp.tool-acquire-timeout/monitoring.mcp.tool-acquire-timeout. - Operators are alerted when the directory sends incomplete change notifications — The health checks (
/observe/doctor) and the Observe Inbox now raise a signal when the connected directory sends change notifications that omitobjectClass. A critical alert appears when such an update could not be completed and was held back — the stored entry keeps its previous values until the directory is fixed; a warning appears when IdentityScribe re-read the full entry from the directory to recover. Each card shows how many occurred in the last 5 minutes and points to the directory-side fix (check the directory’sobjectClassattribute mapping and re-sync the affected entries); the exact attributes the directory dropped are recorded in the ingest logs. The same signals are available for dashboards underscribe.ingest.incomplete_notification.total(reason=repaired/reason=skipped).
Fixed
- Stored entries keep their attributes when the directory sends an incomplete change notification — When IdentityScribe syncs a change from the connected directory, a change notification that arrives missing
objectClass(a mandatory attribute every live entry has) is no longer treated as if the attribute had been removed. Such an update could previously dropobjectClass— and potentially other attributes — from the stored entry, leaving an entry that clients relying onobjectClassmay reject or that attribute searches may miss. IdentityScribe now recognizes the notification as incomplete and re-reads the full entry from the directory; if a complete copy still cannot be obtained, it leaves the stored entry unchanged rather than corrupting it. Incomplete notifications are recorded under the newscribe.ingest.incomplete_notification.totalmetric (reason=repairedwhen the full entry was re-read,reason=skippedwhen the update was held back), so operators can spot a directory that is sending incomplete change notifications. - Pattern-based LDAP bind attributes accept quoted OID keys — Pattern entries under
auth.ldap.bind-attributekeyed by a quoted object identifier (for example"1.2.840.113556.1.4.221") are now read with the correct attribute name. The surrounding quotes were previously kept as part of the name, so any bind that relied on that attribute was rejected. Unquoted dotted keys and ordinary attribute names are unchanged. - LDAP paged-search counts keep useful estimates — Simple paged LDAP responses now keep a server-side estimate when it is available, and the reported size is never lower than the returned page proves. The final page still reports the completed result size when IdentityScribe can determine it.
- Bundled monitoring dashboards use the current planner metrics — The packaged Grafana and Helm dashboards now read the planner metrics introduced in the latest release, so planner-health panels continue to show data after upgrading.
- Forwarded LDAP searches recover from a dropped backend connection — When IdentityScribe forwards a directory search to the connected directory and the backend connection has gone stale — for example after the directory restarted, or an idle connection was dropped by the network — the search now reconnects and retries once instead of returning a “service unavailable” error. First-page and non-paged searches recover automatically; a search already paging through results with a server-side cursor surfaces the interruption so the client can restart the sequence.
- Operator UI API errors keep their request id — An error returned from an Operator UI action now retains the server-assigned request id, so a failure seen in the console can be correlated with its server-side trace and logs.
Changed
- LDAP paging counts now default to exact totals unless a client opts out — LDAP Simple Paged Results and virtual-list-view responses use exact totals by default, so clients that display role or group member counts do not see a low estimate as the total. Clients that only need navigation can still opt out of counts, and operators can choose estimated or no-count behavior through the new count defaults.
- MCP search and lookup default to data attributes — When an MCP client omits
fields, search and lookup now request data attributes (*) by default, matching REST and the shared query default. Clients that need operational attributes can still passfields="+"orfields="*,+", and operators can restore the previous default withchannels.mcp.default-fields = "*,+". - Forwarded directory searches record why a “service unavailable” was returned — Traces and wide logs for a forwarded directory search now carry two diagnostic attributes.
ldap.delegation.pageshows whether the search started a result set (first) or resumed a server-side cursor (continuation);ldap.delegation.retryshows whether an automatic reconnect was not needed (none), recovered the search (recovered), or was exhausted because the directory stayed unreachable (exhausted). Together they separate a transient stale-connection blip that self-healed from a directory that is genuinely down, and flag the one case that cannot be retried automatically — a search resuming a cursor — so it is clear the client must restart that paging sequence.
v3.0.0-rc.11 (2026-07-01)
Added
-
Early warning for memory leaks, with a downloadable diagnostic report — A new always-on sensor watches whether the memory that survives each garbage collection is trending upward without bound. When it is, IdentityScribe logs a warning, raises the
scribe.memory.leak.suspectedmetric (alongsidescribe.memory.live_set.bytesandscribe.memory.growth.per_hour) so unbounded growth shows up in Grafana, and raises a card in the Observe Inbox. From the card’s System → JVM view you can download a memory diagnostic report — a redacted version safe to attach to a support ticket and a full version for local investigation — capturing the JVM’s heap, memory pools, garbage-collector counters, and the sensor’s growth assessment, in the same way as the existing slow-query and workload exports. For deep analysis, an operator with Observe write access can trigger an on-demand heap dump (POST /observe/memory/heap-dump) for tools such as Eclipse MAT; only one runs at a time. Because a heap dump is a full copy of the heap — including credentials and personal data — capture is disabled by default, and enabling it requires settingmonitoring.memory.heap-dump.directoryto a private path (there is no default): the directory and every dump are created readable only by the account IdentityScribe runs as. The redacted report is safe to attach to a support ticket, and the report view’s email action opens a pre-addressed draft for you to attach the downloaded Markdown to. Tune the sensor undermonitoring.memory— thresholds, observation window, and poll interval; it runs by default, independently of whether telemetry export is configured. -
Searches that identify entries by object class resolve without forwarding — A directory search that selects entries by
objectClassorstructuralObjectClass— for example(structuralObjectClass=inetOrgPerson)against a base that several transcribes share — now resolves to the right entry type and is served by IdentityScribe instead of being forwarded to the connected directory.structuralObjectClassfilters are matched the same way asobjectClassfilters. When the directory publishes its schema, IdentityScribe recognizes the structural subclasses of each transcribe’s configured object class automatically, so a transcribe filtered by(objectClass=person)also answersinetOrgPersonandorganizationalPersonsearches with no extra setup; when the schema is unavailable it falls back to the configured classes and logs why. The newtranscribes.<type>.ldap.object-class-aliasesoption lists additional object class values that identify a transcribe for cases automatic detection cannot cover. All of this affects inbound query routing only — it never changes what is synced, stored, or forwarded upstream.
Fixed
-
Memory no longer grows without bound under sustained search load — Continuous LDAP, REST, or GraphQL search traffic caused heap use to climb for the life of the process until garbage collection thrashed and the LDAP channel stopped responding, typically after several hours of uptime. Memory now stays flat under the same load.
-
Substring-search row-count learning stays bounded when persistence is disabled — With
database.gate-learning.enabled = false, the record of which substring-search row counts are exact was never trimmed, so memory could climb slowly under varied substring searches. It is now bounded regardless of that setting. -
Full re-sync startup no longer fails on large stored datasets — When a subscription fingerprint change triggers a full re-sync, IdentityScribe now clears the old stored entries in a way that does not exhaust PostgreSQL lock memory. Large directories no longer fail startup with an
out of shared memorymessage during that reset. -
Searches return each matching entry only once — Free-text and fuzzy searches over several attributes no longer return duplicate rows when the same entry matches through more than one attribute or through several values of a multi-valued attribute. Sorting and cursor pagination keep their existing order.
-
Malformed LDAP paged-search cookies are recorded as client errors — An LDAP Simple Paged Results request with a corrupted continuation cookie already received the correct
protocolErrorresponse; its trace, wide log, and error-rate signals now also classify it as a client error rather than a server-side failure, so a client sending a bad cookie no longer counts against server-error monitoring. -
The eDirectory Identity Manager profile ships in the release archive —
edirectory-idm.conf, documented since v3.0.0-rc.10 as an additive profile to layer on top of your base config, was missing from the downloadable release archive. The archive now includes it under the name used in eDirectory Identity Manager Roles. -
Shutdown waits for sort-index backfill work to stop — When IdentityScribe is backfilling a sort index in the background (for example after adding a multi-valued attribute to
indices.sortable), shutdown now waits for in-progress backfill workers to exit instead of signaling them and returning immediately. A shutdown that must wait past a bounded timeout logs a warning naming the workers still running. -
Cancelled single-entry lookups no longer run their query — A request that resolves to a single entry uses a fast single-row lookup path; if the caller cancelled or timed out while that lookup’s parameters were still being bound, the query could still run against PostgreSQL. It is now skipped as soon as the cancellation is detected, so a request nobody is waiting on no longer does database work.
-
Reference deep links resolve Prometheus metric names, not just OpenTelemetry names —
/ref/*links and the embedded reference index now also recognize a metric’s Prometheus-exposition name (the underscored form scraped from/metrics, for examplescribe_ingest_lag_seconds), alongside its dotted name (scribe.ingest.lag.seconds). Both forms resolve to the same reference page. -
The Getting Started Dockerfile example fails clearly on a bad download — The custom-image
Dockerfileshown in Getting Started now installsca-certificatesand downloads the release archive withcurl -fSL --show-error, so an unauthorized or failed download (for example an expired token) stops the build with a visible error instead of continuing with an invalid archive. -
Observe’s index-build detail list stays within its configured limit during large bursts — When many background index builds complete or fail within the same short window, the detail list behind Observe’s index-build status view could briefly hold more entries than its configured cap. The list is now trimmed to the cap after every burst.
-
Long sortable attribute names no longer report a false index-build failure — A sortable attribute whose combined type-and-attribute name is long (for example the
srvprvlocalizednamesattribute on therequestdefentry type) could show a permanent failed index build in Observe → Storage and log anindex_create_failedwarning on every startup, even though the index and its background maintenance were working correctly. These healthy builds are no longer reported as failed.
v3.0.0 (2026-07-31)
Breaking
-
BREAKING: Negative pagination limits now fail consistently across REST, GraphQL, and MCP — GraphQL directory searches that previously used the configured default now fail, GraphQL change-history errors now use
ARGUMENT_INVALID_VALUEinstead ofINVALID_ARGUMENT, and MCP search and change requests now fail instead of requesting one result. Migration: send a positivelimit, or omit it to use the configured default. -
Monitoring log-rule operation shortcuts are no longer accepted through
monitoring.log.rules[].name— startup fails if the shortcut remains in a rule. Migration: move exact operation filters towhere = "scribe.operation=Exact.Operation"and pattern filters towhere = "scribe.operation~=Prefix.*"; keep any duration or result predicates in the samewhereexpression.monitoring.log.rules = [{ action = exclude, name = "Transcription.WorkItem", where = "duration.seconds<=500ms" }{ action = exclude, where = "scribe.operation=Transcription.WorkItem duration.seconds<=500ms" }] -
Hint persistence operation shortcuts are no longer accepted through
monitoring.hints.persistence.rules[].name— startup fails if the shortcut remains in a rule. Migration: move rule matching towherefilters such ashint-type=CONFIGorseverity=ERROR.monitoring.hints.persistence.rules = [{ action = exclude, name = "CONFIG" }{ action = exclude, where = "hint-type=CONFIG" }]
Added
-
Observe shows where query time goes, and how it trends — Queries gains an Insights tab that separates planning, query-capacity wait, database-connection time, and time to the first result, and charts short-term history for the signals behind a slowdown: query concurrency and rejections, which kind of search is getting slower, ingest lag, throughput and queue pressure, service restarts, and memory growth. The ingest and system views gain matching history panels, and the golden-signal strips become sparklines. A Query Diagnostic Report shows the same breakdown for one captured request, and redacted support exports keep these value-free timings, so performance problems can be investigated without exposing query values. History covers roughly the last two hours at three zoom levels — recent minutes in detail, older data coarser — and lives only in memory: after a restart the charts show an explicit gap and start fresh instead of reporting zeros, as do captures taken before this release. It is on by default and costs well under a megabyte; disable or bound it under
monitoring.stats-history. -
Performance insight report for support — When you report a performance problem, one download now carries the evidence: the performance insight report (Observe → Queries → Insights) packages recent metrics history, query stage timings, the slowest search patterns, ingest health, and database pressure indicators into a single redacted JSON file safe to attach to a support ticket. You need no Prometheus or Grafana of your own. Search identifiers stay stable and usable after redaction, each disclosure tier enforces its documented size limit, and large downloads use gzip when the client accepts it. A full variant for local investigation is available at
/observe/performance-insight/export/full. As with the other reports, the email action opens a pre-addressed draft for you to attach the download to; the report itself is never embedded in the email. -
Repeated searches can use a faster database planning choice — IdentityScribe measures alternatives against live traffic and applies a faster choice to search patterns whose evidence is current, keeping the standard choice active by default. Once the evidence is stable it promotes the choice without an operator enabling it per pattern, and it keeps rechecking, so a choice returns to the standard one when its evidence weakens or changes. Decisions are never stored or keyed by the values you searched for. Observe shows the effective choice, the evidence behind it, and whether a decision is still learning, settled, or being reconsidered; the global learning control remains available for emergency rollback.
-
Observe shows which index build failed and why — When a background index build fails, the Observe → Storage → Build Queue view now lists each failed build by name with the reason it failed, instead of only a failure count. When there are more failures than the list can show, or the details have not loaded yet, the view says so alongside the count. If the same build returns to pending after a completed or failed attempt, the new lifecycle stays visible. Operators can see what needs attention without reading the server logs.
-
Incomplete-notification Inbox cards name the attributes dropped on repair — When the directory sends change notifications without objectClass, the Inbox card for repaired notifications now lists the attribute names recently dropped (for example
objectClass,mail) instead of pointing operators only to ingest logs. It keeps a bounded set of the newest samples per entry type, so one noisy type cannot crowd out the others, and a recording failure never blocks the repaired entry. Samples contain attribute names only, never entry values. -
Browser responses carry baseline security protections — IdentityScribe’s HTTP channels and published documentation now send headers that prevent framing, MIME-type guessing, and referrer disclosure.
Changed
-
The embedded REST API reference is updated — The
/apiand/observereference pages now use the current stable release, including fixes for schema display and keeping request examples in sync. -
Telemetry labels now use bounded, documented values — MCP tool counters and timings use fixed endpoint, tool, and result values; ingest failures use the stable reasons
process,persist, andexecutor; and request-provided attribute names outside the configured catalog no longer create new metric label values. Operator dashboards can group these signals without labels growing from unexpected traffic values.
Fixed
-
Sorted searches with missing sort values are faster and remain complete during concurrent updates — IdentityScribe now chooses the fastest verified complete execution for common first-page searches automatically. Fast searches no longer spend milliseconds on planning work that can exceed the search itself, and entries without the requested sort value cannot be omitted because the data changed between planning and execution. Results, ordering, and cursors are unchanged; no configuration is required.
-
Operator UI presets survive immediate reload or tab close — Saving, updating, or removing a preset previously waited briefly before writing to browser storage, so reloading or closing the page right afterwards could silently discard a change that already appeared saved. Preset changes are now stored immediately.
-
Telemetry shutdown problems are now visible — A failed or timed-out flush of buffered telemetry during shutdown previously looked identical to a clean close. IdentityScribe now logs a warning naming the affected provider and step, so operators can detect telemetry loss at shutdown, and always continues closing the remaining providers.
-
Sizes below one byte no longer render an undefined unit — Fractional byte values in the operator UI displayed output such as
512 undefined; they now always render with a valid unit. -
Large sorted pages no longer fall back too early after finding matching entries — In very large directories, a cursor-paged sorted search with a long list of possible attribute values could switch to slower behavior even while it was still returning matches. A search that is producing results now keeps going for longer before falling back, so the same page returns much faster, with results and ordering unchanged. To restore the previous behavior, set
database.continuation.budget-extension-enabled=false;database.continuation.window.absolute-scan-capremains the hard ceiling. -
Synchronization stops safely after a transcription worker fails — A worker failure could leave accepted entries waiting forever during drain or allow queued work to begin after the failure. IdentityScribe now wakes drain and shutdown waiters, accounts for accepted work that cannot run, refuses later processor admissions, and keeps checkpoint advancement disabled so a restart cannot skip abandoned work.
-
Cursor-paged searches keep requested virtual attributes — A later page that selected a virtual attribute could omit it from the results. Such requests now preserve every requested virtual field on every page.
-
Range filters keep the exact bounds you send — Multiple range conditions on the same attribute could be narrowed through case-insensitive value comparisons before execution, changing the intended result for mixed-case or locale-sensitive values. IdentityScribe now preserves the original bounds and lets the configured directory comparison rules decide the match.
-
Range filters match entries whose bounds are met by different values — Lower and upper bounds on a multi-valued attribute are now evaluated independently, as required by LDAP filter semantics. Entries are no longer omitted when one value satisfies the lower bound and another value satisfies the upper bound.
-
Interrupted database write confirmation is no longer replayed blindly — If the database connection failed while confirming a completed entry update or delete, IdentityScribe could retry work whose outcome was unknown, risking duplicate history or a repeated delete. It now stops the affected worker and keeps its checkpoint pinned until the outcome can be resolved. Failed write preparation also releases partially opened resources.
-
Browser session cookies reject unexpected encryption choices — Session-cookie decryption now accepts only the key-wrapping and authenticated-encryption algorithms IdentityScribe itself uses. Existing valid sessions keep working; cookies that advertise a different algorithm fail authentication before their contents are processed.
-
Cancelled MCP change-history requests stay cancelled — A client cancellation during history streaming was wrapped as a generic server failure. MCP clients now receive the cancellation result, while real history failures keep their existing error handling.
-
Disabled database time limits remain disabled after temporary request settings — A connection that began with no query or lock time limit could retain a temporary limit after a request-specific override. The original disabled state is now restored, preventing unrelated later requests from failing early.
-
Machine-readable tokens stay stable under non-English host locales — Configuration values, HTTP preference handling, GraphQL change types, telemetry labels, export labels, and MCP reference lookups now use locale-independent casing. Hosts using Turkish or another locale with special casing rules produce the same protocol values and accept the same documented inputs as other hosts.
-
Observe’s API reference covers every shipped diagnostic endpoint — The generated Observe OpenAPI document now includes memory snapshots and exports, heap-dump capture outcomes, query diagnostic reports, the setup support report, and the history and performance insight downloads, with their real access levels, disclosure tiers, status codes, and response schemas.
-
Startup fails with a clear error instead of hanging when a service never finishes starting — Services that start in parallel during process startup now share the same five-minute ceiling that sequential startup already enforced. A service stuck in its startup phase previously left the whole process waiting forever without becoming ready or exiting; startup now stops the other services and fails with an error naming the stuck service, so process managers can restart or alert instead of waiting on a process that will never come up.
-
Shutdown during startup no longer briefly starts further services — When a shutdown request arrives in the short window while startup is still bringing service groups up, a group could slip past the cancellation broadcast, start its services after shutdown had already completed, and only then be torn down. Startup now re-checks for a pending shutdown at the moment each group is registered, so no service starts once shutdown has begun.
-
A failed checkpoint read no longer looks like a first-time sync — When reading the stored sync checkpoint failed after retries (for example the database was briefly unreachable during startup), IdentityScribe treated the result as “no checkpoint exists” and started a fresh full synchronization, discarding the saved position. A failed read now surfaces as an error and stops startup of the affected sync worker instead, so the durable checkpoint is preserved and the worker resumes from it once the database is reachable again.
-
The Getting Started curl download examples fail clearly on a bad download — The
curlinstall snippets in Getting Started now use the same fail-fast, redirect-following flags as the Dockerfile example (curl -fSL --show-error). An unauthorized or failed download — for example an expired token — stops with a visible error at download time instead of saving an error response that only fails later atunzip. -
Exported trace names can no longer carry directory or request data — Trace redaction covered the attributes attached to a trace but not the trace’s own name. A request whose URL matched no known endpoint previously used its raw request path — which can contain entry names — as the trace name; such requests are now named by their HTTP method alone. Any trace name outside the expected naming scheme leaves IdentityScribe as
span.redactedinstead of its literal text. Attributes remain governed by the existing redaction policy; no configuration change is needed. -
Two transcribe types can no longer silently share the same storage — Transcribe type names that differ only in non-alphanumeric characters (for example
user-domainanduser.domain) resolve to the same storage, which would mix both types’ entries. Startup now rejects such a configuration with an error naming both colliding types, so the conflict is caught before any data is written. Existing configurations with distinct type names are unaffected. -
Root DSE discovery requesting operational attributes is answered by IdentityScribe — An LDAP Root DSE search (empty base DN, base scope) that requests the all-operational-attributes projection
+— the form many LDAP clients and browsers use for capability discovery — was forwarded to the connected directory, so clients saw the upstream directory’s capabilities instead of IdentityScribe’s. It is now answered locally like every other Root DSE search. Tree-browsing requests on real entries keep their existing forwarding behavior. -
GraphiQL enforces the configured script integrity value — The
channels.graphql.ui.asset.js-srivalue was accepted but never reached the browser, so a tampered script from a custom CDN would still load. The GraphiQL page now carries that integrity value where the browser verifies it before executing the script. Browsers that do not support this check keep the previous behavior; the stylesheet integrity check is unchanged. -
Opening GraphiQL while signed out redirects to the login page — With login enabled and anonymous API access disabled, opening
/graphqlin a browser returned a raw JSON authentication error instead of redirecting to the login page. The browser now lands on the login page and returns to GraphiQL after signing in. Schema and API requests keep the JSON authentication error. -
/readyzreports not-ready when core services are unhealthy — The readiness probe only reflected synchronization readiness, so an instance whose core services (for example the database connection or a channel listener) turned unhealthy after startup kept answering/readyzwithokand stayed in load-balancer rotation./readyznow also requires the services to be healthy, matching its documented “can safely receive traffic” contract./livez,/startedz, and/healthzare unchanged, and sync or index backlogs still do not pull an instance out of rotation. -
Searches naming several entry types with an OR filter are answered — A search over a base that spans several entry types and a filter listing the wanted types as alternatives — for example
(|(type=person)(type=group)), alone or combined with further conditions — was rejected as if no type had been named at all. Such filters now narrow the search to exactly the listed types, the same waytype=personalready did. Filters that mix a type alternative with conditions on other attributes keep the previous behavior, because narrowing those could drop matching entries. -
Breadcrumb links work for entries with long names — In the Operator UI, the breadcrumb trail on an entry page cut the entry identifier at 100 characters when building its link, so clicking a breadcrumb for an entry with a long distinguished name led to a page that does not exist. The link now always carries the full identifier; only the displayed text stays shortened.
-
Observe history panels preserve the evidence that still works — A malformed metric series no longer drops healthy series from the same collection. Query, ingest, JVM, service, and database views distinguish loading, first-load errors, stale data, and unavailable in-memory history; a later refresh or tier-switch failure keeps the last good charts visible with a retry action and stale-data notice.
-
Observe Inbox counts, lists, and details stay in sync — The sidebar count, Inbox list, and recommendation detail now share one bounded-freshness snapshot. Concurrent refreshes share one generation, and applying, dismissing, snoozing, or reopening a recommendation invalidates that snapshot before the next read, preventing a badge count from describing different evidence than the page.
-
Observe history and report downloads use the documented access checks — The in-memory history API now applies the same Observe read access as the other Observe JSON APIs, and the redacted and full report variants each require their documented access level.
-
Performance insight reports include incomplete-notification totals — The support report now includes recent skipped and repaired incomplete-notification counts when history is available. The Inbox card for skipped notifications also stays focused on the held-back updates, rather than listing dropped attributes, which apply only to repaired ones.
-
Heap dump capture no longer reveals server filenames — When on-demand heap dumps are enabled, the capture response now returns an opaque capture ID and size rather than the server location. Repeated capture attempts are also rate-limited with a retry time, so operators get a clear response instead of starting expensive captures back to back.
-
Monitoring configuration mistakes fail validation clearly — Telemetry log filters now reject conflicting operation-name constraints at startup, and one-sided saturation-pressure overrides are checked against the effective default for the other side. Invalid configurations fail with the normal monitoring configuration error instead of surfacing a lower-level startup exception.
-
Exact telemetry log filters stay exact — Monitoring log filters that name one operation exactly no longer also match near-named operations. Wildcard and regular-expression operation filters keep their existing pattern behavior.
-
Observe keeps configured attribute names in index-build labels — Storage build status now preserves the original configured attribute descriptor when reporting planned index work, so mixed-case attributes remain recognizable in the Operator UI instead of being shown through a normalized fallback.
-
Support downloads use safe filenames — Browser-triggered report downloads now sanitize unusual suggested names before setting the saved filename, preventing directory separators or control characters from appearing in download names.
v3.1.0 (2026-08-31)
Added
- Query diagnostics expose each stage — The Query Diagnostic Report now breaks a captured operation’s time into Normalize, Plan, Prepare, and Execute stages and shows how much time preparation used. The share covers one executed search or count operation; Execute includes admission wait, database work, and result delivery.
Breaking
-
Memory pressure metric matches documentation — BREAKING: Prometheus now exports JVM memory pressure as
jvm_memory_pressureinstead ofjvm_memory_pressure_ratio— custom dashboards and alerts that queryjvm_memory_pressure_ratiostop receiving samples. Migration: replacejvm_memory_pressure_ratiowithjvm_memory_pressure; the bundled dashboards are already updated. -
Workload evidence separates retained examples — BREAKING: Observe workload responses and support exports now separate aggregate observations from bounded retained examples — clients that read
seenCount, card recurrence fields, orsamplescan no longer parse those removed fields, and workload support exports now use schema 2. Migration: readobservedCount,evidenceCoverage, andretainedExamples; useretainedBecauseto explain each example; update support-export consumers to schema 2. -
Affected entry histories repair automatically — BREAKING: On upgrade, histories affected by the former identity reset are repaired automatically when the stored evidence is unambiguous, restoring the original entry ID as the single identity, removing the false Created event, and reducing later changes to the attributes that actually changed — temporary entry IDs created during affected refreshes no longer resolve. Migration: re-resolve affected entries by UUID or distinguished name and refresh any cached entry IDs; ambiguous histories remain unchanged and available for support diagnostics.
Fixed
-
Entry history survives configuration changes — Existing entries retain their entry ID and complete history when synchronized attributes or other source settings are added, changed, or removed and a full refresh follows. Only real object changes create history events; newly configured attributes that are absent on an object and attributes removed from synchronization update the current view without inventing changes. An interrupted or incomplete refresh remains pending instead of deleting entries that were not safely checked.
-
Attribute Coverage lists every attribute — The Operator UI now lists every stored attribute, including attributes without index or sort coverage. Those rows use a neutral “Not applicable” state and do not report healthy coverage signals.
-
Slow shares use every operation — Observe calculates each slow share from the complete workload seen by the current process. Drill-downs keep a bounded typical comparison beside noteworthy examples, while the overview stays focused on actionable patterns. Slow or failed examples no longer act as the denominator for the aggregate.
v3.2.0 (2026-09-18) Latest
Added
- Technical accounts can authenticate locally — Configure one or more local accounts with a DN, password, and optional short-name alias for LDAP, HTTP Basic, or browser login without a backend authentication round trip. Forwarded LDAP operations use the account’s explicit backend credentials or the root LDAP credentials as a complete pair, and disabled delegation fails before a backend connection is opened.
- Thread dumps from a running service — Sending the quit signal (
kill -3) now writes every thread’s state and code location to the service output and lets the process carry on serving. Operators can see what a stalled instance is waiting on without restarting it, and the dump holds no directory entries or credentials. Earlier versions shut down on that signal.
Changed
- Authentication settings keep their method scope — LDAP and ROPC cache lifetimes, disabled caching, and token-expiry revalidation now apply to the selected authentication method. Concurrent checks for the same backend credentials share bounded work; local passwords are never cached, and bearer validation remains unchanged.
Fixed
- LDAP sign-ins stop waiting indefinitely — Authentication and identity lookups now have a ten-second default timeout, with limited simultaneous upstream work. Configure
auth.ldap.request-timeoutfrom 1 millisecond to 1 minute. Brief bursts can wait up to two seconds at each capacity limit within the authentication deadline. The default pending limit is 256 callers, including those joining a check for the same credential. Sizeauth.ldap.max-pendingfor the client connections that may bind within one upstream authentication round-trip, such as a client pool reconnecting after a restart; adjust waiting withauth.ldap.capacity-wait. Capacity rejections return LDAP busy (51), and authentication timeouts return unavailable (52); HTTP Basic sign-ins return 503 for either. These temporary failures skip the credential-failure delay. Invalid credentials retain LDAP 49, HTTP 401, and their configured minimum response duration.