All versions since v3.0.0-rc.10 (2026-06-25)
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.