Upgrading IdentityScribe
Upgrade IdentityScribe between versions safely. This guide focuses on breaking changes and the smallest steps to restore a healthy system.
If you haven’t deployed IdentityScribe before, start with the Deployment and Configuration guides instead.
Organized by upgrade path (e.g., v2.x → v3.0).
From v2.x to v3.0
Section titled “From v2.x to v3.0”This section covers upgrades from any v2 release to v3.0.
TL;DR (upgrade checklist)
Section titled “TL;DR (upgrade checklist)”- Update environment variables to the unified
SCRIBE_*prefix. - Migrate renamed config paths (
monitoring.telemetry.*→monitoring.*,hints.*→monitoring.hints.*). - Migrate indices configuration from
ldap.indicesandtranscribes.*.ldap.indicesto new paths. - Migrate HTTP configuration to the unified
http {}server and (optionally) named sockets. - Update probes and tooling to the new endpoints (
/observe/*,/observe/health/*,/metrics,/*z). - Update dashboards/queries for the new metric namespace (
scribe.*/scribe_*). - Update virtual attribute templates (
{{current.*}}→{{self.*}}). - Fix broad queries that now require an explicit type constraint.
- Update any client-side error-code matching to
SCREAMING_SNAKE_CASE.
After deploying, verify:
curl -fsS http://localhost:8080/readyzcurl -fsS http://localhost:8080/metrics | headcurl -fsS http://localhost:8080/observe/doctor | jqcurl -fsS http://localhost:8080/observe/config | jq
Config restructure
Section titled “Config restructure”Some configuration paths were reorganized for consistency. Old paths are ignored.
What changed
Section titled “What changed”monitoring.telemetry.*→monitoring.*hints.*→monitoring.hints.*
Migration
Section titled “Migration”# Oldmonitoring.telemetry.traces.enabled = truehints.enabled = true
# Newmonitoring.traces.enabled = truemonitoring.hints.enabled = trueVerify
Section titled “Verify”- Start IdentityScribe and check the resolved config at
/observe/config(secrets are redacted). - If you rely on hints endpoints, confirm
/observe/hintsand/observe/signaturesare reachable (or explicitly disabled).
Indices configuration
Section titled “Indices configuration”Index configuration moved from ldap.indices to a dedicated indices {} block at root and transcribe levels. Indices control how the local database indexes transcribed data and are transcribe-specific, not LDAP-specific.
What changed
Section titled “What changed”| Old Path | New Path |
|---|---|
ldap.indices.* | indices.* (root level) |
transcribes.<type>.ldap.indices.* | transcribes.<type>.indices.* |
| Old Environment Variable | New Environment Variable |
|---|---|
SCRIBE_LDAP_INDICES | (removed - was unused) |
| (none) | SCRIBE_INDICES_PARTIAL_MATCH |
| (none) | SCRIBE_INDICES_VALUE_MATCH |
| (none) | SCRIBE_INDICES_SORTABLE |
Migration
Section titled “Migration”# Oldldap { indices { partial-match = "cn, sn" }}transcribes { user { ldap { base = "ou=users,o=data" indices { value-match = "uid, mail" } } }}
# Newindices { partial-match = "cn, sn"}transcribes { user { ldap { base = "ou=users,o=data" } indices { value-match = "uid, mail" } }}Verify
Section titled “Verify”- Start IdentityScribe and check the resolved config at
/observe/config. - Verify that
/observe/indexesshows the expected index configuration for each transcribe.
Env prefix
Section titled “Env prefix”Environment variable prefixes are standardized. If you still use the old prefixes, your settings will not be applied.
What changed
Section titled “What changed”IDENTITY_SCRIBE_*→SCRIBE_*SCRIBE_TELEMETRY_*→SCRIBE_*
Migration
Section titled “Migration”# OldIDENTITY_SCRIBE_READONLY=trueSCRIBE_TELEMETRY_TRACES_ENABLED=true
# NewSCRIBE_READONLY=trueSCRIBE_TRACES_ENABLED=trueSee the Configuration guide for naming conventions and examples.
Logging
Section titled “Logging”Logger configuration has been consolidated to five canonical loggers with standardized environment variable overrides. Old logger keys are removed.
Migration
Section titled “Migration”| Old | New |
|---|---|
log.EventStore / log.Scribe | log.Ingest |
log.Settings | log.Config |
log.LDAP, log.AsyncSearch, log.TaskExecutor | Removed (use monitoring/wide logs instead) |
New canonical loggers:
log.SuperVisor(SCRIBE_LOG_SUPERVISOR)log.Ingest(SCRIBE_LOG_INGEST)log.Monitoring(SCRIBE_LOG_MONITORING)log.License(SCRIBE_LOG_LICENSE)log.Config(SCRIBE_LOG_CONFIG)
Example:
log { level = "info"
Ingest = "debug" Config = "warn" Monitoring = "off" # Disable wide-log output entirely}See the Log Configuration Reference for the full set of logger settings and format options.
HTTP sockets
Section titled “HTTP sockets”All HTTP traffic (REST API, monitoring endpoints, site/docs) uses a unified HTTP server configured under http {}. You can keep everything on one port, or split traffic using named sockets.
For full details and patterns, see Networking.
Migration (single socket)
Section titled “Migration (single socket)”# Everything on one porthttp { host = "0.0.0.0" port = 8080}Migration (split monitoring to an internal port)
Section titled “Migration (split monitoring to an internal port)”http { host = "0.0.0.0" port = 8080
sockets.internal { host = "localhost" port = 9001 }}
monitoring.socket = "internal"Endpoints
Section titled “Endpoints”Several operational endpoints were renamed and legacy routes were removed. No aliases are provided, so probes and dashboards must be updated.
What changed
Section titled “What changed”| Old | New |
|---|---|
/status | /observe/status |
/status/indexes | /observe/indexes |
/status/hints | /observe/hints |
/status/signatures | /observe/signatures |
/healthy | /observe/health/ready (JSON) or /readyz (plain text) |
/ (metrics) | /metrics |
/-/* legacy monitoring routes | Removed |
Note: /observe now serves interactive OpenAPI documentation for the Observe endpoints. The status JSON moved to /observe/status.
Kubernetes probes
Section titled “Kubernetes probes”Use the plain-text aliases:
livenessProbe: httpGet: path: /livezreadinessProbe: httpGet: path: /readyzstartupProbe: httpGet: path: /startedzHints endpoints
Section titled “Hints endpoints”The monitoring.hints.endpoints.* configuration is no longer supported. Hints endpoints use fixed paths under /observe/*.
What to do
Section titled “What to do”- If you used custom paths, update your tooling to the fixed endpoints:
/observe/hints/observe/signatures
- If you don’t use hints endpoints, disable them explicitly:
monitoring.hints.persistence.enabled = falsemonitoring.hints.explain.enabled = falseSee the Monitoring Configuration Reference for the full hints configuration.
Metrics names
Section titled “Metrics names”Metric names are now standardized to the scribe.* namespace (Prometheus auto-converts dotted names to snake_case scribe_*).
Update Prometheus queries and Grafana dashboards to use the new names. If you depend on specific label keys/values, verify them against the Telemetry Reference.
Metric name mapping
Section titled “Metric name mapping”| v2.x Metric | v3.x Metric | Notes |
|---|---|---|
scribe_directory_queries_pressure | scribe_query_permit_pressure | Same semantics |
scribe_directory_pipeline_duration_seconds{stage,outcome} | scribe_query_stage_duration_seconds{phase,result} | Label normalization |
scribe_directory_query_time_seconds{op,result} | scribe_channel_request_duration_seconds{channel,op,result} | Front door metric |
scribe_tasks_pressure{entryType} | scribe_ingest_task_pressure{entry_type} | Label key normalization |
scribe_transcription_queue_pressure{entryType} | scribe_ingest_queue_pressure{entry_type} | Label key normalization |
scribe_events_count_total{entryType,event,op,target} | scribe_ingest_events_written_total{entry_type,event_type} | Collapsed labels |
scribe_service_transition_seconds{service,type} | scribe_service_restarts_total{service} + scribe_service_up{service} | Simplified |
scribe_hints_query_signature_*{signature} | Removed | Use /observe/signatures |
scribe_directory_query_*{attribute} | Debug-only or removed | Use /observe/stats/* |
scribe_query_sql_chars, scribe_query_sql_params | Removed | Trace events instead |
Endpoint mapping
Section titled “Endpoint mapping”| v2.x Path | v3.x Path |
|---|---|
/status | /observe/status |
/status/indexes | /observe/indexes |
/status/hints | /observe/hints |
/status/signatures | /observe/signatures |
/status/stats/* | /observe/stats/* |
Label key mapping
Section titled “Label key mapping”| v2.x Label | v3.x Label |
|---|---|
entryType | entry_type |
protocol | channel |
outcome | result |
service (with wrappers) | service (normalized, no wrappers/ports) |
Result value mapping
Section titled “Result value mapping”| v2.x Value | v3.x Value |
|---|---|
success | ok |
error, failure, deadline, etc. | See Error Handling for the full list of result kinds |
Virtual attributes
Section titled “Virtual attributes”Virtual attribute templates use new placeholder names.
What changed
Section titled “What changed”{{current.*}}→{{self.*}}
Migration
Section titled “Migration”# Oldfilter = "(member={{current.entryDN}})"
# Newfilter = "(member={{self.entryDN}})"If legacy syntax is detected, IdentityScribe fails fast on startup with a clear, actionable error.
Query scoping
Section titled “Query scoping”Broad searches that could span multiple entry types now require an explicit type constraint. Ambiguous queries are rejected early with a clear error.
What to do
Section titled “What to do”- Add a type constraint such as
entryType=<type>or an identifying condition likeobjectClass=...to broad queries. - If you rely on wildcard bases or very broad filters, test queries in staging and update filters/config accordingly.
Error codes
Section titled “Error codes”Error codes are now standardized to SCREAMING_SNAKE_CASE. If your clients match on error codes, you must update them.
See Error Handling for the stable error contract and troubleshooting workflow (including which IDs/headers to include when opening support tickets).
GraphQL type naming and partition hardening
Section titled “GraphQL type naming and partition hardening”IdentityScribe now treats similar type and attribute spellings consistently across GraphQL and database partitions. The upgrade process includes automatic hardening and clear startup errors when naming collisions are detected.
What changed
Section titled “What changed”- Predictable GraphQL names — Entry types with hyphens, underscores, or mixed case (for example
organizational-unit,organizational_unit,OrganizationalUnit) resolve to the same GraphQL type and field names. Invalid leading characters (for example digits) get an underscore prefix. - Safer partition handling — On startup, IdentityScribe verifies that each partition name is bound to the expected type. If the name already belongs to a different type, startup stops with a clear error.
- Automatic legacy repair — Upgrade automatically repairs legacy
entries_datatype partition names to canonical names (entries_data__for_<type>). The repair is idempotent. - Early sanitize-collision checks — Startup now fails before partition creation when two entry-type spellings or two observed attributes would normalize to the same partition target.
What to do
Section titled “What to do”For most upgrades, no manual action is required. The upgrade steps run automatically.
If startup fails, check logs for one of these errors and apply the matching fix:
| Error | Fix |
|---|---|
partition name collision | A partition with the target name already exists for a different value. Rename or drop the conflicting partition, then restart. |
canonical name X exists for value Y but legacy Z is bound to W | Upgrade found a real naming conflict in existing partitions. Resolve the conflicting partition names/values manually, then restart so startup can complete. |
entry-type sanitize collision: canonical partition X maps to both 'Y' and 'Z' | Use one consistent spelling for each entry type across all transcribes (e.g. inetOrgPerson everywhere, not inetorgperson in one place). |
observed-attribute sanitize collision: attributes 'X' and 'Y' both map to partition Z | Use distinct catalog attribute names. Hyphen/underscore/case differences can collide after normalization, so keep one canonical form per attribute. |
Verify
Section titled “Verify”After upgrade:
- Confirm service starts and upgrade checks complete without errors.
- Confirm GraphQL exposes the expected normalized names (GraphiQL or introspection).
- Run
curl -fsS http://localhost:8080/readyzandcurl -fsS http://localhost:8080/observe/doctor.
REST/OpenAPI naming hardening
Section titled “REST/OpenAPI naming hardening”REST and OpenAPI now apply strict startup validation for type-derived names.
What changed
Section titled “What changed”- Inflector collision checks — startup fails when two entry types would produce the same REST path or OpenAPI
operationIdafter singular/plural normalization. - Strict OpenAPI component naming — generated schema component names are sanitized to conservative identifiers (
^[A-Za-z][A-Za-z0-9_]*$) for broad code-generator compatibility. - Fail-fast on sanitized collisions — if two types sanitize to the same component key, startup stops with a clear collision error.
What to do
Section titled “What to do”- Audit transcribe type names for singular/plural ambiguity (
uservsusers, etc.). - If startup reports collisions, rename one side to a distinct canonical type name and redeploy.
- Regenerate API clients from
/api.jsonor/api.yamlafter renames.
After upgrade: quick verification
Section titled “After upgrade: quick verification”Once the service is running, a quick “is it healthy?” sweep:
curl -fsS http://localhost:8080/readyzcurl -fsS http://localhost:8080/metrics | headcurl -fsS http://localhost:8080/observe/doctor | jqcurl -fsS http://localhost:8080/observe/channels | jqRelated
Section titled “Related”- Deployment — Installation and platform-specific setup
- Configuration — HOCON, env vars, inheritance patterns
- Health and Monitoring — Health checks and dashboards
- Signals — Golden signals and error classification
- PromQL Recipes — Alert queries and dashboards