Skip to content

Version History

Version 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 return 401 unless an operator session or accepted credential is present. Migration: For trusted local evaluation, set monitoring.observe.auth.enabled=false or SCRIBE_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=true now fail startup. Migration: Configure auth.http.ssl.trust-store with the provider CA, remove the insecure setting, or keep auth.http.ssl.insecure=true only for local development.
  • Credentialed CORS cannot reflect every origin — HTTP configurations that combine allow-credentials=true with 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 changes tool accepts since/until time bounds, range=all for unbounded history, and before for 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-suites setting 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=all with 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.
  • Documentation improvements — 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

  • Search correctness and pagination — Paginated searches now handle several edge cases that could omit, duplicate, or misorder results:

    • 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.
  • Operator UI reliability and display

    • Opening /ui now 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.
  • 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/changes now 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_stalled gauge increments and the ingest.sync_stalled doctor check turns degraded. The readiness snapshot reason changes from coherent-restored-baseline to restored-baseline-sync-incomplete. Set monitoring.ingest.fail-readyz-on-failed-state=true to 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.
  • REST, GraphQL, and LDAP API correctness

    • 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 null for 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=jdoe resolve under the requested entry type’s configured base. Generic node(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: objectClass and 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.
  • 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.