All versions since v3.0.0-rc.8 (2026-06-22)
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. - Planner telemetry uses one consistent naming scheme — The metrics and trace attributes that describe how the query planner executed a search were spread across several inconsistent names and namespaces. They now live under a single
scribe.plan.*metric namespace, and a single per-search-strategy latency histogram replaces the former gate-only one — so every routed search is measured, not only the gated ones. Migration: Dashboards or alerts keyed on the previous planner metric names (scribe.directory.plan.*,scribe.planner.*,scribe.database.plan_mode.*) or on the retiredscribe.gate.query.durationhistogram must be repointed to thescribe.plan.*series — per-search latency is nowscribe.plan.route.duration.seconds. Custom queries that grouped planner metrics by older route, gate-source, or dispatch-leading attributes should move to the consolidatedscribe.plan.route,scribe.plan.lead,scribe.gate.route, andscribe.gate.route_sourceattributes; reason labels are shortened under the new schema. - 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 the query planner’s preparation cost — The planner does extra database work around a search — readiness and sizing checks it runs before the result query itself — and that cost used to be invisible. The Observe Inbox evidence-health panel now adds an “Estimation & GUC cost” section showing how much per-query sizing work ran and which connection pools still pay a per-request connection-setup cost, so you can spot pools that would benefit from a baked session profile. The underlying timing is exported for your own dashboards, and with trace export on (
monitoring.traces.enabled = true) the background estimation and plan-mode checks show up on the trace timeline with their timing and frequency. Numbers read “evidence pending” until the first sample arrives. The same per-query sizing and connection-setup cost also appears in a query’s diagnostic report. 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, while ensuring 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) Latest
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.