UnboundID LDAP SDK for Java 5.1.0

UnboundID LDAP SDK for Java version 5.1.0 has been released and is available for download from GitHub and SourceForge, and it is available in the Maven Central Repository. The release notes provide a pretty comprehensive overview of the changes since the previous 5.0.1 release, but here’s a summary:

  • We fixed an issue in which the JVM-default trust manager did not always correctly handle cross-signed issuer certificates when the presented chain included an expired issuer certificate. It will now check to see if it can build a valid path with an alternate trust anchor.
  • We added a new SchemaValidator class that can identify all kinds of problems with LDAP schema definitions. We also provide a new validate-ldap-schema command-line tool that will examine definitions contained in one or more LDIF files and report any problems that it finds.
  • We updated the in-memory-directory-server command-line tool to validate any schema definitions provided through the --useSchemaFile argument. Even if there are problems, the server will still try to use that schema to the best of its ability (as was previously the case). The --doNotValidateSchemaDefnitions argument can be used to disable the new validation if it is not desired.
  • We added a new ldappasswordmodify command-line tool that can be used to perform a self password change or an administrative password reset. It supports the password modify extended operation (as described in RFC 3062), and it can also change passwords using a regular LDAP modify operation or using an Active Directory-specific modification.
  • We added three new command-line tools for performing operations on data contained in LDIF files:

    • The ldifsearch tool can be used to identify entries that match a given set of search criteria.
    • The ldifmodify tool can be used to apply a set of add, delete, modify, and modify DN changes to LDIF data.
    • The ldif-diff tool can be used to identify differences between data in two provided LDIF files and report the differences in the form of LDIF change records.
  • We added a new version of the ldapcompare tool that can be used to perform LDAP compare operations in a directory server. The new version offers a lot of additional functionality like support for performing multiple compare assertions and using a variety of request controls, and it can generate parseable output in tab-delimited text, CSV, or JSON formats.
  • We updated the in-memory directory server to make it possible to add custom attributes to the root DSE. While it was already possible to replace the entire root DSE entry with a static entry, this new approach makes it possible to retain some dynamic content (for example, changelog-related attributes) while still customizing other attributes.
  • We made several changes in our support for entries with the ldapSubEntry object class:

    • We added a new RFC3672SubentriesRequestControl class with support for the LDAP subentries request control as described in RFC 3672.
    • The LDAP SDK already had support for an alternate version of the control described in draft-ietf-ldup-subentry through the SubentriesRequestControl class, but that class has been deprecated in favor of a new DraftLDUPSubentriesRequestControl class, which helps avoid confusion with the class that implements the RFC 3672 version of the control. The deprecated class is still fully functional and will be kept to preserve backward compatibility, but we recommend updating code that uses the old class for the sake of clarity.
    • The in-memory directory server has been updated with support for the RFC 3672 version of the control. It already had support for the draft-ietf-ldup-subentry version.
    • The in-memory directory server has been updated so that it will return entries with the ldapSubEntry object class if the filter includes an “(objectClass=ldapSubEntry)” component.
    • The ldapsearch command-line tool has been updated with support for the RFC 3672 version of the LDAP subentries control, using the new --rfc3672Subentries argument. It already had support for the draft-ietf-ldup-subentry version of the control through the --includeSubentries argument, and that argument is still available, but we now recommend using --draftLDUPSubentries instead for the sake of clarity.
  • We updated the ldapsearch tool to add a new “values-only” output format (as an alternative to the existing LDIF, tab-delimited text, CSV, and JSON output formats). If this output format is selected, then it will only output the values of the requested attributes without any entry DNs or attribute names. This can help extract raw attribute values from a directory server from a script without the need for any additional text processing.
  • We updated the ldapsearch tool to add a new --requireMatch argument. If this argument is provided and the search completes successfully but does not return any entries, then the tool will have an exit code of 94 (corresponding to the noResultsReturned result code) rather than zero. This argument does not have any visible effect on the output.
  • We updated the round-robin and fewest connections servers sets to expose the blacklist manager that they use to avoid trying to establish connections to servers that are believed to be unavailable.
  • We updated the manage-certificates tool to make it easier to list and export certificates from the JVM’s default trust store without needing to know the path to the appropriate file.
  • We improved the logic that the LDAP SDK uses when selecting ordering and substring matching rules for ordering operations involving attributes that are defined in the schema but whose definition does not specify an ordering matching rule. It will now try to infer an appropriate ordering matching rule from the equality matching rule before trying other alternatives like inferring a rule from the associated syntax or using a default rule.
  • We updated the LDAP command-line tool framework to make it easier and more convenient to communicate securely with the Ping Identity Directory Server (and other related server products). This includes:

    • We added a new TopologyRegistryTrustManager class that can use information in the server’s topology registry to determine whether to trust the certificates for instances in the topology.
    • If no trust-related arguments are specified when running the tool, it will now check the server’s default trust store and the topology registry to determine whether the presented certificate should be trusted. It will still also check the JVM’s default trust store, and it will still fall back to interactively prompting the user if the certificate cannot be trusted through other means.
  • We streamlined the process that LDAP command-line tools use to establish and authenticate connections when run in interactive mode. It will now recommend TLS encryption over unencrypted communication with a simplified set of arguments, and it will recommend simple authentication over unauthenticated connections. Further, when the tool is part of a Ping Identity Directory Server (or related server product) installation, it will read the configuration to determine the appropriate port to suggest when connecting to the server.
  • We made several improvements to the summarize-access-log tool that can be used to examine Ping Identity Directory Server access logs. These include:

    • You can now customize the maximum number of values to display for each item. It was previously hard-coded to use a limit of 20 values. If any values were omitted, then it will now tell you how many were left out.
    • You can now choose to de-anonymize the output to obtain the specific attribute values used in search filters and entry DNs (instead of displaying question marks as placeholders).
    • The output will now include information about the most common TLS protocols and cipher suites used for secure communication.
    • The output will now include the most common successful and failed bind DNs and the most common authentication mechanisms.
    • The output will now include the most common DNs used as alternate authorization identities (e.g., via the proxied authorization request control).
    • The output will now include the most common filters used for unindexed searches, the most common base DNs for searches with non-baseObject scopes, the filters for searches taking the longest to complete, and the most common filters for searches returning zero, one, or multiple entries.
    • When summarizing the most commonly invoked types of extended operations, the tool will now try to provide a human-readable name for the extended operation in addition to its OID.
  • We added client-side support for obtaining password policy state information from the Ping Identity Directory Server’s ds-pwp-state-json virtual attribute.
  • We added client-side support for the new populate composed attribute values and generate server profile administrative tasks in the Ping Identity Directory Server.
  • We added a new OID.parseNumericOID method that can be used to parse a provided string as a valid numeric object identifier, optionally performing strict validation. If the provided string does not represent a valid numeric OID, then the method will throw an exception with a message that explains the problem.
  • We improved the error messages generated for problems that may arise when parsing schema definitions.
  • We updated the schema parsing code so that it can now handle schema elements with a description value that is an empty string. Although empty descriptions (or other types of quoted strings) are not permitted in schema element definitions, some servers allow them. Empty descriptions are still not allowed by default, but that behavior can be overridden with a code change or a system property.
  • We added a new IA5 string argument value validator that can be used to require that the values of associated arguments are only permitted to contain ASCII characters. The manage-certificates tool has also been updated to provide better validation for certificate components that are required to be IA5 strings, including DNS names and email addresses in the subject alternative name extension.
  • We added support for encoding and decoding timestamps in the ISO 8601 format described in RFC 3339.
  • We updated the LDAP command-line tool framework so that if the --help-sasl argument is used in conjunction with a --saslOption argument that specifies the name of the SASL mechanism, the output will only include help information for that mechanism.
  • We fixed a bug in the StaticUtils.isASCIIString method that caused it to only look at the lowest byte for each character in the provided string.
  • We added new ByteStringBuffer utility methods, including getting individual bytes or sets of bytes at a specified position, for determining whether the buffer starts with or ends with a given set of bytes, and for reading the contents of a file or input stream into the buffer.
  • We added new StaticUtils convenience methods for reading and writing files as bytes, strings, or lists of lines.
  • We added support for new password policy state account usability warning and notice types for the Ping Identity Directory Server. The new types can be used to indicate that the account has too many outstanding authentication failures, but that the server will take some other action (for example, delaying the bind response) instead of completely preventing authentication.
  • We fixed an issue in the LDAP SDK’s JSON-formatted debug logging support for debug messages containing exceptions with another exception as the underlying cause.
  • We fixed an issue with the command-line tool framework that could prevent it from setting an argument value from a properties file even though that same value would have been permitted if it had been provided directly on the command line.
  • We updated the default standard schema provided with the LDAP SDK to include additional attribute syntaxes, matching rule, attribute type, and object class definitions.

UnboundID LDAP SDK for Java 5.0.1

The UnboundID LDAP SDK for Java is a fast, powerful, user-friendly, and completely free Java library for communicating with LDAP directory servers and performing other LDAP-related processing. We have just released version 5.0.1 of the LDAP SDK, and it is available for download from GitHub and SourceForge, as well as from the Maven Central Repository. The release notes are available online at https://docs.ldap.com/ldap-sdk/docs/release-notes.html.

This is a minor release that was primarily created in service of an upcoming release of the Ping Identity Directory Server, as it fixes an issue in a tool that only impacts that new release. Nevertheless, there are a couple of additional updates, so we’re making it publicly available.

The changes over the previous 5.0.0 release include:

  • We added a new LDAP connection logger API that can be used to keep a record of processing performed by the LDAP SDK, including successful and failed connection attempts, operation requests and responses (including non-final responses like search result entries, search result references, and intermediate responses), and disconnects. The LDAP SDK includes a connection logger instance that formats messages as JSON objects, but it’s an extensible API, so you’re free to create your own implementation using whatever format you want.
  • We have updated the LDAP command-line tool framework to make it possible to specify the address of the target directory server(s) using either –host or –address as an alternative to the existing –hostname argument.
  • We fixed an issue that prevented the collect-support-data tool from running properly in local mode when using a secure connection (either SSL or StartTLS). This functionality only applies to an upcoming release of the Ping Identity Directory Server, so existing installations should not have been affected, and new installations will have the fix.
  • We made minor updates to the usage output for several command-line tools to improve wording and fix typos. We also fixed typos in other messages used throughout the LDAP SDK.

UnboundID LDAP SDK for Java 5.0.0, now available under the Apache License

The UnboundID LDAP SDK for Java is a fast, powerful, user-friendly, and completely free Java library for communicating with LDAP directory servers and performing other LDAP-related processing. We have just released version 5.0.0 of the LDAP SDK, and it is available for download from GitHub and SourceForge, as well as from the Maven Central Repository. The release notes are available online at https://docs.ldap.com/ldap-sdk/docs/release-notes.html.

The most significant change in this new release is that the LDAP SDK is now available under the terms of the Apache License, Version 2.0, which is a very permissive OSI-approved open source license. Although it was already open source under the terms of the GNU GPLv2 and LGPLv2.1, the Apache License imposes fewer restrictions on how you can use the LDAP SDK. You are no longer required to offer to redistribute the source code (even if you want to use a modified version), and there’s no longer any concern about whether you need to keep the LDAP SDK jar file as a separate component. The Apache License is well respected and is often seen as more compatible and easier to use in non-open-source software than the GNU license, so we hope that this will make it easier to use in your applications, whether open source or proprietary. The LDAP SDK is still available for use under the terms of the GPLv2 and LGPLv2.1 (as well as the non-open-source UnboundID LDAP SDK Free Use License), but we recommend that new users consider using it under the Apache License.

Aside from adding the new license, we made several code changes in this release as well. They include:

  • The LDAP SDK offers an LDAPConnectionDetailsJSONSpecification class that allows you to define a JSON file with all of the settings needed to create and authenticate individual LDAP connections or connection pools. We’ve updated this class so that it’s now possible to indicate that when establishing a connection that is secured with SSL or StartTLS, the LDAP SDK should automatically trust any certificates signed by an authority in the JVM’s default set of trusted issuers. This was already the default behavior if you didn’t provide your own trust store (or choose to blindly trust all certificates, which isn’t recommended for production use), but it’s now possible to use this option in conjunction with a provided trust store so that it’s possible to trust a certificate either through that trust store or through the JVM’s default set of trusted issuers.
  • The KeyStoreKeyManager can be used to obtain a certificate from a key store file if one is needed during TLS negotiation. We have updated this class to provide an option to better validate that the key store can actually be used by this purpose with the settings that you provide. If you use this option and supply the alias of the certificate you wish to use, then the key manager will now verify that the alias exists in the key store, that it’s associated with a private key entry (as opposed to a trusted certificate entry, which only contains the public portion of a certificate and isn’t suitable for use if you need to present that certificate to the peer), and that all of the certificates in the chain are currently within their validity window. If you don’t specify a certificate alias, then the validation will make sure that the key store contains at least one private key entry in which all of the certificates in the chain are within their validity window.
  • The TrustStoreTrustManager can be used in the course of determining whether to trust a certificate presented by a peer during TLS negotiation. We have improved performance and concurrency for this trust manager by eliminating unnecessary synchronization that forced interaction with the trust store to be single-threaded.
  • We fixed an issue that could interfere with GSSAPI authentication if a JAAS login module configuration was loaded and cached by the JVM before the login attempt. In such cases, the cached configuration could be used instead of the one that was intended.
  • The LDAPDebuggerRequestHandler can be used to log detailed information about LDAP requests and responses that pass through an application using the LDAP SDK’s LDAPListener framework (including the in-memory directory server and the ldap-debugger command-line tool). We fixed an issue that could cause messages to be held up in an internal buffer rather than immediately written out as soon as they’re logged. In some cases, this could significantly delay the appearance of these messages or could prevent them from being written out at all if the amount of data to be logged was never enough to fill that internal buffer.
  • We added a new JSONAccessLogRequestHandler to the LDAPListener framework. This can log information about requests and responses as JSON objects, which are both human-readable and machine-parseable. While the existing AccessLogRequestHandler produces output that can be parsed programmatically to some extent, it is more optimized for human readability.
  • The LDAP SDK offers debugging logging support that can be helpful in diagnosing problems whose cause may not otherwise be readily apparent. Previously, the debug messages were logged in a form that was primarily intended to be human-readable rather than machine-parseable. They are now written in a JSON format that is both human-readable and machine-parseable.
  • The manage-certificates command-line tool provides a utility for interacting with certificate key and trust stores in the Java JKS format or the standard PKCS#12 format. When displaying detailed information about certificates in a key or trust store, the tool may not have been able to properly decode public key information for certificates with 384-bit elliptic curve public keys, and it also may not have been able to properly decode a subject alternative names extension that included one or more directoryName values. While it was still possible to display most of the information about the affected certificates, the updated version can now provide the full details about those elements.
  • The Ping Identity Directory Server includes a collect-support-data utility that can be used to gather a variety of information from a server installation that can be very useful for troubleshooting problems, tuning performance and scalability, and better understanding the environment in which the server is running. Previously, this utility could only be invoked by logging into the system on which the server instance is running and running the command-line tool. We have now added a couple of additional mechanisms for running the utility. It can now be invoked via an administrative task (either as an individual event that is requested by a remote client or as a recurring task that runs on a regular basis) that will create the resulting support data archive in a specified location on the system (which may be a shared filesystem for easier exfiltration). It can also be invoked via an extended operation that will run the tool and stream its output and the resulting support data archive back to the client in the form of intermediate response messages. Further, although the logic for actually collecting all of this support information remains in the server, we have added the collect-support-data command-line tool to the LDAP SDK so that it is easier to invoke the tool against a remote server without needing to install the server software on the client system.
  • The Ping Identity Directory Server provides a monitor backend that authorized clients can use to obtain a wealth of useful information about the state of the server, and the LDAP SDK includes support for retrieving and parsing the information in these monitor entries. We have updated the LDAP SDK’s support for the general monitor (that is, the top-level “cn=monitor” entry) to make it easier to obtain information about the cluster with which the server is associated, the location of the server instance, and a unique identifier that was generated for the server when the instance was initially configured.
  • The LDAP SDK offers a Version class that provides version information for the LDAP SDK, including the version number and information about the repository (e.g., the repository URL and revision ID) from which the LDAP SDK source code was obtained. This information was previously only offered as public static final constants, but referencing these constants from third-party applications could lead to unexpected behavior thanks to a “feature” of the Java compiler that will directly imbed the values of those constants (even if they come from a separate library) in the Java bytecode that it generates. This means that if your application references these LDAP SDK version constants and you compile it against one version of the LDAP SDK, then those version constants will be placed directly into the compiled bytecode. If you upgrade the LDAP SDK version that you use without recompiling your application (e.g., by just replacing the LDAP SDK jar file with a newer version), the code referencing the LDAP SDK version would still have the old values. To address this, we have updated the Version class to provide methods for obtaining the values of all the version constants. If you use these methods to obtain the values rather than referencing the constants directly, then you will always get the correct LDAP SDK version information even if you update the LDAP SDK without recompiling your application.

Ping Identity Directory Server

We have just released version of the Ping Identity Directory Server, along with new releases of the related Directory Proxy Server, Data Synchronization Server, Metrics Engine, and Delegated User Admin products. The release notes include a comprehensive list of features, enhancements, and fixes, but here are some of the most notable changes included in the release:

  • We have expanded support for the manage-profile tool to include the Directory Proxy Server, Data Synchronization Server, and Data Governance Server products. This allows you to set up, update, or reconfigure a server using the information in a provided profile. The profile defines the configuration, schema, extensions, certificates, encryption settings, and all the other components needed to configure a server instance exactly the way you want it.
  • We have updated the Directory Proxy Server so that it can use the topology registry to automatically discover and start using Ping Identity Directory Server instances without needing to change the Directory Proxy Server configuration.
  • We have improved our support for integrating with third-party monitoring services like Splunk by updating the stats collector plugin to support sending data in StatsD format to a specified endpoint. We have also updated the periodic stats logger so that it supports generating JSON-formatted output. The former CSV output format is also still supported. And we have added a new “Status Health Summary” monitor entry that provides a summary of the server’s current assessment of its health, which especially simplifies monitoring with third-party monitoring over JMX.
  • We have updated the Directory Server so that it now supports SCIMv2 in addition to the existing SCIMv1 and Directory REST API options for REST-based access to directory data. Formerly, SCIMv2 was only available through the Data Governance Server.
  • We have added a new replace-certificate tool that makes it easier to replace a server’s listener or inter-server certificate. The tool offers a non-interactive mode that is suitable for scripting support, but it also has a full-featured interactive mode that can walk you through the process of obtaining and installing a new certificate. The interactive mode will also provide you with the necessary commands to achieve the same result in non-interactive mode.
  • We have dramatically improved our support for account status notifications. We have defined a couple of new notification types that can be raised whenever an entry is created or modified by a request that matches a given set of criteria. We have also defined many new properties that can be used in the notifications. And we have added a new multi-part email account status notification handler that can be used to send plain-text and/or HTML-formatted email messages whenever an appropriate event occurs within the server.
  • We have added a new password validator that leverages the Pwned Passwords service to make it easier to reject passwords that are known to have been compromised in data breaches.
  • We have added a new password storage scheme that uses the Argon2i password hashing algorithm, which was selected as the winner of a 2015 password hashing competition.
  • We have updated our support for the PBKDF2 password storage scheme so that it offers additional variants that leverage the 256-bit, 384-bit, and 512-bit SHA-2 digest algorithms. We have also updated the default salt length and iteration count values in accordance with NIST SP 800-63B recommendations.
  • We have improved the server’s support for generating passwords. We have added a new request control that can be included in add requests to have the server generate a password for the new entry and return it to the client in a corresponding response control. We have also added a new extended operation that can be used to request that the server generate one or more passwords that can be provided to the end user as new password suggestions when creating an account or changing a password.
  • We updated the Data Synchronization Server’s password sync agent for Active Directory so that it encodes passwords using a salted 256-bit SHA-2 digest rather than the previous salted SHA-1 digest. The SHA-1 digest can still be used if necessary for purposes of backward compatibility.
  • We updated the Data Synchronization Server’s create-sync-pipe-config tool to add support for using the PingOne for Customers service as a sync source or destination.
  • We updated Delegated Admin’s support for constructed attributes. Constructed attributes can now be made read-only, and they can also reference other constructed attributes. Constructed attribute values can now also be updated when any of their dependent attributes change.
  • We updated the HTTP external server configuration to make it possible to specify the alias of the certificate chain to be presented during mutual TLS negotiation.
  • We added a new JVM-default trust manager provider that can be used to automatically trust any certificate signed by one of the trusted issuers in the JVM’s default trust store.
  • We have added a new Server SDK API for sending email messages.
  • We updated the exec task to make it possible to specify the current working directory for the command that is being executed. The server previously always used the server root as the current working directory, and that is still the default if no alternate path is specified.
  • We updated the collect-support-data tool to add a --duration argument that will cause it to capture log content for the specified duration up to the current time.
  • We fixed an issue that prevented assured replication from being honored for requests received via SCIM or the Directory REST API.
  • We fixed an issue in which the restore tool might not have automatically restored all of the dependencies of an incremental backup.
  • We fixed an issue in which the Directory Proxy Server could incorrectly report a success result for an entry-balanced search operation in which all attempts in a backend set failed with a timeout.
  • We updated log file rotation listeners, including the summarize access log and copy log file listeners, so that they perform their processing in a background thread. This can help ensure that their processing does not temporarily block logging attempts on very busy servers.
  • We fixed an issue in which the verify-index tool could report spurious error messages when examining index keys containing multi-byte UTF-8 characters.
  • We fixed an issue in which escaped special characters in schema extensions may not be handled properly. This could cause unexpected or incorrect behavior in cases where those values are interpreted by the server (for example, in the X-VALUE-REGEX constraint in attribute type definitions).
  • We fixed an issue that could cause access log messages for bind and StartTLS operations to report the client connection policy that was previously in use for the connection rather than the new policy that was assigned as a result of the associated operation.

UnboundID LDAP SDK for Java 4.0.14

We have just released version 4.0.14 of the UnboundID LDAP SDK for Java. It is available for download from the releases page of our GitHub repository (https://github.com/pingidentity/ldapsdk/releases), from the Files page of our SourceForge repository (https://sourceforge.net/projects/ldap-sdk/files/), and from the Maven Central Repository (https://search.maven.org/search?q=g:com.unboundid%20AND%20a:unboundid-ldapsdk&core=gav).

The LDAP SDK release notes are available at https://docs.ldap.com/ldap-sdk/docs/release-notes.html, but the changes included in this release are as follows:

  • Fixed an issue in which LDAP URLs with consecutive percent-encoded bytes were not decoded correctly.
  • Fixed an issue that could cause the LDAP SDK to incorrectly handle data read from a server when the communication was protected with SASL integrity or confidentiality. Thanks to Boris Danilovich for reporting the problem and identifying the cause.
  • Fixed an issue that prevented the searchrate tool from running if neither a base DN pattern nor an LDAP URL pattern was provided.
  • Improved the logic that the LDAP SDK used when selecting the cipher suites to use when establishing a TLS-secured connection. Weaker suites are disabled, and the enabled suites are prioritized so that those offering forward secrecy and stronger encryption are preferred.
  • Added a new FullLDAPInterface that extends LDAPInterface and adds support for close, bind, and processExtendedOperation methods. The existing LDAPConnection, AbstractConnectionPool, and InMemoryDirectoryServer classes have been updated to implement this interface.
  • Added a new non-final MockableLDAPConnection class that makes it easier to mock an LDAPConnection instance. It implements FullLDAPInterface and wraps a provided LDAPConnection. If you create a MockableLDAPConnection subclass, then you may override any of the FullLDAPInterface methods to provide whatever logic you desire for them. Any non-overridden methods will invoke the corresponding method on the provided LDAPConnection instance.
  • Fixed a minor typo in the ldapsearch usage information

UnboundID LDAP SDK for Java 4.0.13

We have just released version 4.0.13 of the UnboundID LDAP SDK for Java. It is available for download from the releases page of our GitHub repository, from the Files page of our SourceForge repository, and from the Maven Central Repository.

This is a minor update that is primarily intended to serve the upcoming release of the Ping Identity Directory Server, but it also includes some useful debugging enhancements and improvements in its support for X.509 certificates. The full release notes are available online, but the primary changes included in this release are as follows:

  • Added support for debugging connection pool interactions, including checking out and releasing connections, as well as establishing and closing connections for use in the pool.
  • Fixed an issue in the prompt trust manager that could cause it to incorrectly display a warning for some certificates with a basic constraints extension that included the optional path length constraint.
  • Updated the manage-certificates check-certificate-usability command to add an additional check to see whether the certificate at the root of the chain is found in the JVM’s default set of trusted issuer certificates. If it is not found, the tool will display a notice, but it will still complete with a success result.
  • Fixed an issue in manage-certificates that could prevent it from correctly showing the key agreement usage when displaying verbose information about a certificate with the key usage extension.
  • Fixed an issue that could prevent properly decoding an authority key identifier extension that included the optional authorityCertIssuer element in an X.509 certificate.
  • Made the ManageCertificates.readCertificatesFromFile method public so that it can be used outside of the LDAP SDK. This method can be used to read a set of PEM-encoded or DER-encoded X.509 certificates from a specified file.
  • Made the ManageCertificates.readCertificateSigningRequestFromFile method so that it can be used outside of the LDAP SDK. This method can be used to read a PEM-encoded or DER-encoded PKCS #10 certificate signing request from a file.
  • Updated the passphrase-encrypted output stream to provide an option to override the default key factory iteration count.
  • Updated support for the exec task to add an option to specify the path to use as the current working directory when invoking the specified command. Previously, the server would always use the server instance root directory, and that will still be the default if no alternate working directory is specified.
  • Added an additional StaticUtils.getEnvironmentVariable method variant that can be used to provide a default value that should be used if the specified environment variable is not set.
  • Added an additional StaticUtils.getStackTrace method variant that allows you to limit the number of stack frames to include from code before the call into the LDAP SDK. Also, updated StaticUtils.getExceptionMessage when invoked for a NullPointerException so that it now shows all frames from the LDAP SDK (and anything that the LDAP SDK calls), and up to three frames from the code before the call into the LDAP SDK.

UnboundID LDAP SDK for Java 4.0.12

We have just released version 4.0.12 of the UnboundID LDAP SDK for Java. It is available for download from the releases page of our GitHub repository (https://github.com/pingidentity/ldapsdk/releases), from the Files page of our SourceForge repository (https://sourceforge.net/projects/ldap-sdk/files/), and from the Maven Central Repository (https://search.maven.org/search?q=g:com.unboundid%20AND%20a:unboundid-ldapsdk&core=gav).

The LDAP SDK release notes are available at https://docs.ldap.com/ldap-sdk/docs/release-notes.html, but the changes included in this release are as follows:

  • Fixed an issue in the write timeout handler that could prevent it from properly cleaning up a timer task object for a connection if an attempt to establish that connection failed. This regression, which was introduced in the 4.0.11 release, could lead to a gradual increase in memory consumption over time.
  • Updated the write timeout handler so that it will now shut down its background thread after all LDAP connections have been closed.
  • Fixed an issue with the JVM-default trust manager that could cause it to incorrectly abort TLS negotiation if the server presented only a partial certificate chain, and if the last certificate in that partial chain was not included in the JVM’s default set of trusted issuers but was signed by one of those issuers.
  • Corrected the result code used in the LDAPException that is thrown when attempting to parse a malformed schema element. We now use the correct INVALID_ATTRIBUTE_SYNTAX result code instead of the INVALID_DN_SYNTAX result code that had been used by mistake.
  • Fixed an issue in the way that the persistence framework constructed LDAP attributes for its internal processing. While it would have properly selected an appropriate matching rule based on the data type of the corresponding Java field when constructing attribute type definitions for inclusion in the server schema, it neglected to use that matching rule for client-side matching involving those attributes, but instead always used a default “case-ignore string” matching behavior.
  • Updated the manage-certificates tool to use the SHA-1 digest algorithm instead of 256-bit SHA-2 when generating the subject key identifier extension for certificates and certificate signing requests. This makes it possible to work around a limitation in Microsoft certificate authorities, which are apparently unable to handle CSRs with 256-bit subject key identifiers.
  • Fixed an issue in the search-and-mod-rate tool in which the search durations reported by the tool included not only the time required to process the search, but also the time required for the associated modify operations. Further, if the tool was configured to limit the rate at which modify operations would be attempted, the reported search durations could also include any wait imposed by the rate limiter.
  • Added client-side support for the SCRAM-SHA-1, SCRAM-SHA-256, and SCRAM-SHA-512 SASL mechanisms.
  • Added client-side support for a “generate password” request and response controls. When included in an add request sent to the Ping Identity Directory Server, the request control indicates that the server should generate a password for the entry and return it to the client in the corresponding response control. The ldapmodify tool has been updated to provide support for this control.
  • Added client-side support for a “generate password” extended operation. When sent to the Ping Identity Directory Server, this operation will cause the server to generate one or more passwords that may be suggested to the end user when creating or updating a user entry.
  • Updated the transform-ldif tool to provide options to exclude LDIF records by change type, and to exclude LDIF records that do not have a changetype.
  • Updated the command-line argument parser to provide a better error message if the value the user provides to a string or Boolean value argument is not in the set of allowed values for that argument. The error message will now include a list of the allowed values.
  • Updated the command-line tool interactive mode processor so that when it prompts for a password, PIN, or other sensitive value that does not get echoed to the screen, it will now ask the user to confirm the value to help ensure that they entered it correctly.
  • Updated the command-line tool interactive mode processor so that when the user asks to see the set of arguments that will be used when running the tool, it will now display the full command rather than just listing the arguments. Further, if the command spans multiple lines, then all but the last line will now include a trailing backslash. This makes it more convenient to run the command non-interactively because it can simply be copied and pasted.
  • Updated the argument parser to provide a more convenient way to define mutually dependent argument sets, such that if any argument in the set is provided, then all of the other arguments will also be required.
  • Updated the argument parser to allow applications to define their own custom interactive mode rather than using the default one that the LDAP SDK provides.
  • Added a set of StaticUtils.linesToString convenience methods that can convert a list or array of strings to a single string that includes line breaks after each line.
  • Added a set of StaticUtils methods for obtaining all of the addresses associated with the network interfaces available on the system, and to get the canonical host names associated with those addresses.

UnboundID LDAP SDK for Java 4.0.11

We have just released version 4.0.11 of the UnboundID LDAP SDK for Java. It is available for download from the releases page of our GitHub repository (https://github.com/pingidentity/ldapsdk/releases), from the Files page of our SourceForge repository (https://sourceforge.net/projects/ldap-sdk/files/), and from the Maven Central Repository (https://search.maven.org/search?q=g:com.unboundid%20AND%20a:unboundid-ldapsdk&core=gav).

The LDAP SDK release notes are available at https://docs.ldap.com/ldap-sdk/docs/release-notes.html, but the changes included in this release are as follows:

  • Updated the round-robin and fewest connections server sets so that they can temporarily blacklist a server that was found to be offline or unavailable. If an attempt to create a connection to a server fails, or if that connection is found to be unacceptable for some reason (e.g., it does not pass the associated health check), subsequent connection attempts will avoid that server until a background thread determines that it is available again. Blacklisted servers will still be tried as a last resort if it is not possible to get an acceptable connection to a non-blacklisted server. These server sets will now use the blacklist by default, but that can be disabled programmatically through the constructor or by setting a system property before creating the server set.
  • Updated the round-robin and fewest connections server sets to improve concurrency. In previous implementations, these sets could only create one connection at a time, which could limit the rate at which connection pools using them could establish new connections. This is no longer the case, and any number of threads will be able to create connections in parallel using the server sets. This change also updated the ServerSet API to make it possible for a server set to be notified whenever a connection created with that set has been closed.
  • Added a new SubtreeDeleter utility class that can make it easier to delete a specified subtree, optionally including or excluding the base entry for that subtree. It provides a good client-side alternative to the subtree delete request control, which isn’t supported by all servers and can sometimes be problematic in servers that do support it.
  • Added a new ldapdelete command-line tool that can be used to delete entries from an LDAP directory server. The DNs of the entries to delete can be provided on the command line, read from a file, or read from standard input. Alternately, the server can search for and delete all entries matching one or more filters. It offers a number of options, including support for client-side and server-side subtree deletes, rate limiting, and a variety of standard and proprietary controls.
  • Improved the LDAP SDK’s protection against socket write attempts that block for an indefinite length of time. This is only likely to occur when sending a large number of asynchronous requests over a connection, and only in the case that the server stops reading requests from the client or if a networking problem prevents the request from reaching the server and prevents the client from receiving any information about that failure.
  • Added InMemoryDirectoryServer.applyChangesFromLDIF methods that can be used to read LDIF change records and apply them to data in the server. The changes will be applied atomically, and if any of them cannot be applied successfully, then the server data will remain unchanged.
  • Updated the searchrate utility to allow specifying the base DN, scope, filter, and requested attributes using LDAP URLs rather than using separate arguments to provide appropriate values. The LDAP URL can be a fixed URL, or it can be a value pattern (including the ability to include variable content in the URLs or to load the URLs from a file). Using LDAP URLs allows for more precise control over the combination of base, scope, filter, and requested attributes on a per-request basis. Note that any addresses and ports used in the URLs will be ignored; the --hostname and --port arguments will still be used to identify which servers to use.
  • Updated the ldapsearch and ldapmodify command-line tools to use an unlimited response timeout, which will prevent the tool from giving up on an operation if it takes the server a long time to return any kind of response. Previously, the tools used the LDAP SDK’s default timeout of five minutes for searches and 30 seconds for add, delete, modify, and modify DN operations.
  • Updated the ldapmodify command-line tool to add a --clientSideSubtreeDleete argument that can be used to cause each delete operation to be converted to a client-side subtree delete operation, in which the tool will search for entries to delete and then delete them individually. This makes it easier to delete entries with subordinates on servers that either do not support the subtree delete request control or in which the client may not have permission to use that control.
  • Added a new indent-ldap-filter command-line tool that can help make it easier to visualize complex filters with a lot of components, and especially a lot of nesting. If possible, it can also try to simplify the filter (for example, to remove unnecessary levels of nesting, like an AND inside an AND).
  • Enabled concurrent socket factory use by default for all versions of Java. In the past, we have observed that at least some IBM JVMs had a thread safety issue with SSL socket factory implementations, so we only allowed a socket factory to be used concurrently by multiple threads on a whitelisted set of JVMs. We no longer believe that the IBM JDK socket factory thread safety is an issue, and there are now many more JVM vendors (e.g., Apple, Azul, Amazon Coretto, AdoptOpenJDK, and potentially Red Hat), so concurrent socket factory use will be enabled by default. If an issue is found on a particular JVM, then concurrent access can be disabled programmatically or with a system property.
  • Updated the LDAPCommandLineTool API to add an option to expose an --enableSSLDebugging argument. If this argument is available, and if it is provided in the set of command-line arguments when the tool is run, then the JVM’s SSL/TLS debugging support will be enabled, and the JVM will write a large amount of TLS-related debugging information to standard error. This can help troubleshoot problems with or provide detailed information about any TLS communication that the tool attempts.
  • Updated the LDAP SDK to add protection against JVM security managers that may prevent calls to certain methods, like attempts to interact with system properties, environment variables, or logger levels.
  • Updated the password reader so that it will generate a more user-friendly error message if it is run in a context in which no console is available. A tool could encounter this error if its output has been redirected, or if it’s not running in an interactive shell (for example, in a cron job or system startup script).
  • Dramatically improved the performance of the streamfile value pattern, which operates like the sequentialfile value pattern in that it can iterate through values in sequential order, except that streamfile doesn’t need to hold the whole file in memory at once whereas sequentialfile does.
  • Updated the Filter.simplifyFilter method to simplify an AND filter containing an LDAP false filter (an OR filter with zero components, which will never match anything) to just that LDAP false filter, and to simplify an OR filter containing an LDAP true filter (an AND filter with zero components, which will match any entry) to just that LDAP true filter.
  • Added a PasswordValidationDetailsResponseControl.get(LDAPException) method that makes it more convenient to get the response control from an unsuccessful operation.
  • Improved the exception message that is generated if a failure occurs while trying to create a TLS-based connection. If the JVM supports creating an unconnected SSLSocket and then connecting it after the fact (which makes it possible to specify a connect timeout), and that connection attempt failed (for example, because the client did not trust the certificate presented by the server), the LDAP SDK could think that the connection was still established. Subsequent attempts to use the connection would fail, but the failure message would not accurately reflect the true cause of the problem.
  • Updated the in-memory directory server to improve the diagnostic message that is returned when it rejects an add attempt because the provided entry is not within any of the configured base DNs.
  • Fixed an issue in generating the normalized representation of a multivalued RDN when one or more of those components referenced an attribute type by its OID or by a name other than the first one listed in the attribute type definition. Previously, the normalized string representation would have simply used an all-lowercase representation of the provided attribute name, but it will now use an all-lowercase representation of the primary name for that attribute (if schema information is available to the client). Also, updated the logic used to determine whether an RDN has a specified name or name-value pair to handle the use of alternate names, and exposed the RDN.getNameValuePairs method to make it easier to work with an RDN’s name-value pairs.
  • Fixed a bug in the ByteStringBuffer.append(CharSequence,int,int) method in which the final integer argument could be interpreted as the number of characters to append rather than the end position at which to stop appending, which could yield incorrect results when the method was called with a nonzero start position. Also, updated the ByteStringBuffer.append methods that take CharSequence arguments to eliminate the creation of an intermediate character array, thereby improving performance and reducing garbage creation.
  • Updated the LDAP SDK’s command-line tool framework to fix an issue with the tool’s validation for required, exclusive, and dependent argument sets. If an argument was configured with a default value, then that default value could have been mistakenly treated as if it had been explicitly provided by the user. This could cause problems for arguments that are part of an exclusive argument set (in which only one of the arguments in that set may be provided) or a dependent argument set (in which an argument can only be used if at least one of a specified set of additional arguments is present). In such cases, the tool could not have been used in interactive mode. The modrate tool was affected by this issue.
  • Updated the argument parser to fix a problem with the way that it handles backslash characters in argument property files. Previously, it only correctly handled backslashes if they were at the end of a line to indicate that the content continued to the next line, or if they were followed by the letter ‘u’ and the hexadecimal representation of the desired Unicode character. It did not handle the backslash in front of another character used to force that character to be treated as a literal (for example, a backslash followed by an equal sign should be treated as just an equal sign, but was instead being treated as a backslash followed by an equal sign).

Naming Entries With entryUUID in the Ping Identity Directory Server

Choosing an entry’s RDN is something that shouldn’t be taken lightly. Ideally, it should meet all of the following criteria:

  • It needs to be unique so that it doesn’t conflict with the RDNs of any other entries beneath the same parent.
  • It should be something that’s not likely to change so that clients don’t have to worry about performing modify DN operations.
  • It should be something that doesn’t contain any personally identifiable or otherwise sensitive information. DNs are often included in log messages, and if a client has permission to see any part of an entry, then they’ll be able to see its DN.
  • It shouldn’t be something predictable. An attacker shouldn’t be able to guess the DN of a specific user, or even of any user in the server.

This means that things like usernames, common names, email addresses, and telephone numbers aren’t good choices. Account numbers are also not great because they tend to follow predictable patterns (e.g., sequentially increasing numbers).

What you really want is something that is basically random and has enough entropy to ensure that you won’t get an accidental conflict and so that an attacker will be unlikely to guess a valid value. It would be easy enough for a client to generate a long-ish random string to use for this purpose, but it turns out that the directory server (at least, a server that supports RFC 4530) already generates just such a value for each entry: its entryUUID.

Of course, there’s a catch-22 problem with using the entryUUID attribute as the naming attribute for an entry: the client doesn’t know what the entryUUID is going to be because it’s generated by the server. The client can’t specify it because the entryUUID attribute type is declared with the NO-USER-MODIFICATION constraint.

One potential workaround would be to create an entry with a throwaway value for the RDN, figure out what the entry’s entryUUID value is (using either the post-read control or by issuing a search to retrieve the entry), and issue a modify DN operation to rename the entry using that value. But that’s a hassle, and it puts undue burden on both the client and the server. Fortunately, if you’re using the Ping Identity Directory Server, then you have a couple of additional options:

  • The client can include the “name with entryUUID” request control in the add request.
  • The server can be configured so that any add request matching a specified set of criteria automatically gets created with entryUUID as its naming attribute.

Each of these will be described in more detail below.

The Name With entryUUID Request Control

The name with entryUUID request control may be included in an add request to indicate that the server should replace the RDN with the provided entry with one that uses the name and value of the entryUUID attribute the server generated for the entry. This control has an OID of “” and no value. We recommend that it be marked critical so that the add attempt will fail if the server cannot honor the request.

When using this control, the client should supply a DN for the entry that indicates the location in the DIT where the new entry should reside, but the RDN for the DN doesn’t really matter because it’s going to get replaced with the entryUUID. If you want, you can use an attribute value from the entry to add (just like if you were adding the entry without the control), but you can also use a bogus name-value pair. For example, you could provide a DN of “replaceWithEntryUUID=replaceWithEntryUUID,ou=People,dc=example,dc=com”, and the server would add the entry with a DN like “entryUUID=4869eea6-90bf-45bf-9fcb-eac096564bc8,ou=People,dc=example,dc=com” (although of course the entryUUID would vary each time).

Of course, there is one big issue with using this control: when the entry is added, the client won’t know what the entry’s actual DN really is. The way that we address that is to treat an add request that includes the name with entryUUID request control as if it also included a post-read request control with a single requested attribute of entryUUID. This will cause the add response to include a post-read response control with the DN and entryUUID value for the entry that was added. If you want additional attributes from the entry, you can explicitly include a post-read request control along with the name with entryUUID request control in the add request with the attributes you want to retrieve.

We provide support for the name with entryUUID request control in the ldapmodify command-line tool through the --nameWithEntryUIUD argument. For example:

$ bin/ldapmodify --hostname ds.example.com \
     --port 636 \
     --useSSL \
     --bindDN "cn=Name With entryUUID Example,ou=Applications,dc=example,dc=com" \
Enter the bind password:

The server presented the following certificate chain:

     Subject: CN=ds.example.com,O=Ping Identity Self-Signed Certificate
     Valid From: Saturday, April 27, 2019 at 11:11:58 AM CDT
     Valid Until: Saturday, April 23, 2039 at 11:11:58 AM CDT
     SHA-1 Fingerprint: 41:5f:72:4a:e0:d0:22:18:3e:59:90:6f:65:fc:fe:34:f1:39:84:68
     256-bit SHA-2 Fingerprint: 54:d5:58:07:bd:af:8b:b4:19:8e:03:a3:c5:14:0d:2a:e6:1e:c2:3a:29:6c:17:5f:5f:61:97:1d:31:3d:2b:ac

WARNING:  The certificate is self-signed.

Do you wish to trust this certificate?  Enter 'y' or 'n': y
# Successfully connected to ds.example.com:636.

dn: replaceWithEntryUUID=replaceWithEntryUUID,ou=People,dc=example,dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: test.user
givenName: Test
sn: User
cn: Test User
userPassword: testUserPassword

# Adding entry
# replaceWithEntryUUID=replaceWithEntryUUID,ou=People,dc=example,dc=com ...
# Result Code:  0 (success)
# Post-Read Response Control:
#      OID:
#      Post-Read Entry:
#           dn: entryUUID=7866e6d4-faa7-40e4-bad0-9ef26e566efd,ou=People,dc=exa
#            mple,dc=com
#           entryUUID: 7866e6d4-faa7-40e4-bad0-9ef26e566efd

Since the control doesn’t have a value, it’s easy enough to use in any LDAP API that supports controls (although you may find it a chore to get the DN of the resulting entry if that API doesn’t also support the post-read response control). But if you’re using the UnboundID LDAP SDK for Java, we provide direct support for the control through the NameWithEntryUUIDRequestControl class. I’ve written a simple AddEntryNamedWithUUID program to demonstrate how to use this class to add an entry with the request control and get its DN.

Automatically Naming Entries With entryUUID

Although it’s pretty simple to use the control in an add request to explicitly indicate that an entry should use entryUUID as the naming attribute, this does require the client to know about and use the control. This isn’t always possible, but the Ping Identity Directory Server has you covered there as well. You can configure the server so that any add request that matches a specified set of criteria will automatically be treated as if it included the name with entryUUID request control. This option is available through the following pair of properties in the global configuration:

  • auto-name-with-entry-uuid-connection-criteria
  • auto-name-with-entry-uuid-request-criteria

For example, if you wanted to configure the server so that any entry added with the “person” object class will behave as if it included the name with entryUUID request control, you would use a configuration like the following:

dsconfig create-request-criteria \
     --criteria-name "Adds of Person Entries" \
     --type simple \
     --set operation-type:add \
     --set "any-included-target-entry-filter:(objectClass=person)"

dsconfig set-global-configuration-prop \
     --set "auto-name-with-entry-uuid-request-criteria:Adds of Person Entries"

At this point, adding an entry with the “person” object class from any client will cause that entry’s RDN to be replaced with one generated based on the entryUUID operational attribute. The response will include the post-read response control as if the request had included the name with entryUUID request control (although the client will likely not know to look for it).

So I guess SLAMD is a thing again…

The year was 2002. I had recently jumped ship from Netscape to Sun Microsystems after AOL bought Netscape and decided they wanted out of their iPlanet alliance. I was working as a sustaining engineer on whatever Sun’s brilliant marketeers decided to call their LDAP directory server at the time. One day, my boss, Steve Shoaff, came into my office with a couple of ideas. He said that he wanted me to build a tool that could measure the directory server performance with a lot of load by hitting it from multiple clients at the same time. And he said that he wanted to call it “SLAMD”, which is a play on “slapd”, which kind of stands for “standalone LDAP daemon” and is used in the process names of some directory server products.

So I built it, and I think that it’s fair to say that it turned into something substantially more impressive than either of us originally imagined. It had a Java-based API that you could use to define the types of workloads that you wanted to process, a web-based interface that you could use to schedule jobs and view the results (numerically and graphically), and a client that you could install on the systems that you wanted to used to drive load against the server. Over time, I added new types of jobs and lots of other features, like self-optimizing jobs (which repeatedly run the same job with different amounts of client load to find the optimal performance), job groups (which let you schedule several jobs to run in succession), and resource monitoring (which lets you monitor system statistics like CPU, disk, and network utilization on various systems).

SLAMD was pretty good at what it did, and it worked with all types of LDAP-compliant directory servers, so it became one of the preeminent directory server benchmarking tools. We convinced Sun to open source it, and lots of people started using it. It could be used for things other than directory servers, too (I did build some basic support for other protocols like HTTP, POP3, IMAP, and SMTP, and the ability to interact with relational databases), but LDAP performance and stress testing was always its big wheelhouse.

Fast forward several years, and SLAMD was still pretty great but was starting to show its age, at least under the covers. I started working on it in the Java 1.3 days, before nice features like generics, foreach, concurrency APIs, sub-millisecond timing, and so much more. The web interface was all hand-crafted HTML and mostly contained in one giant source file, and it was getting pretty unwieldy. I did make some attempts to try to modernize it, but I got really busy with other things, like creating OpenDS as a replacement for Sun’s stagnating C-based directory server, then moving on from Sun to launch UnboundID and working furiously to build up its directory server, directory proxy server, and LDAP SDK products.

Shortly after Sun and I parted ways, Oracle bought Sun and gradually started killing off most of the good things about it. This included shutting down the java.net site, which had been the open source repository for SLAMD, and I decided to take that opportunity to just let it kind of fade away. I figured it might be better to start something new from scratch, with a much more modern foundation, than to try to give SLAMD the kind of makeover I thought it needed. Of course, that was nearly a decade ago, and while I’ve done a lot since then, creating a new directory server benchmarking tool (other than a handful of command-line tools like searchrate and modrate that we ship with the LDAP SDK) hasn’t really been in the cards. Meanwhile, SLAMD is still getting a surprising amount of use. Even though it’s not so easy to get your hands on it anymore, people were still getting their hands on it and using it.

After having the topic come up several times in the last few weeks, I finally bit the bullet and dusted off the old code. I spent a couple of weekends doing some pretty extensive code cleanup. I fully generified everything, so there aren’t any more build warnings about raw types. I pulled in much more modern versions of dependencies like Apache Tomcat, the Berkeley DB Java Edition, and the UnboundID LDAP SDK for Java. I reorganized some of the jobs, including putting some legacy stuff out to pasture, and I wrote new versions of several of them. I split up some of the admin interface code into separate source files to make it more manageable, and I made some minor user interface enhancements.

So anyway, I went ahead and put the updated code on GitHub at https://github.com/dirmgr/slamd. Since no single entity owns the copyright on the code, it’s not possible to change the license, and it will therefore always will be licensed under the terms of the Sun Public License version 1.0. I’m not promising that I’ll add any major new features, but it’ll at least be more readily available than it has been, and with some more modern guts.

For now, if you want to use it, you’ll need to check it out and build it for yourself (there’s a README that tells you how to do that). Just know that it’s not backward-compatible with the version that I last touched in 2010, so don’t try to upgrade an existing instance (but if you do want the code for that old version, just check out revision 5777f3e5d78ff03985af4e68670e649127339c59, since I used it to seed the new repository).

Also note that there’s still a lot more work to do. There’s quite a bit more code cleanup that’s still on my to-do list (it builds cleanly with Java 8, but there are several deprecation warnings with Java 11). I plan on rewriting some more of the jobs (including making some potentially-incompatible changes). I know that some of the resource monitoring is broken (at least on Linux, which isn’t so concerned about maintaining consistent output in some of its commands). I haven’t touched any of the documentation. I’ve only done a very minimal amount of testing so far. So while it’s fine to play around with what’s there now, and please report issues if you find them, just know that I reserve the right to make even more non-backward-compatible changes as I continue to modernize the code.