SyntaxHighlighter Evolved: LotusScript Brush

SyntaxHighlighter Evolved: LotusScript Brush is a small WordPress plugin that adds support for the LotusScript language to the great SyntaxHighlighter Evolved plugin by Alex Mills.

I have submitted a request to add the plugin to the official WordPress plugin directory. The review process will take some time.

In the meantime, you can start using SyntaxHighlighter Evolved: LotusScript Brush right now.

Download the plugin from here , unpack it and upload to your /wp-content/plugins directory.

Update 04-FEB-2019: The plugin is now available in the WordPress plugin directory.

Next activate the plugin

SyntaxHighlighter Evolved: LotusScript Brush will now be available as a new language in the Block Editor (“Code Languages”)

SyntaxHighlighter Evolved: LotusScript Brush highlights classes, method and properties, keywords, strings, comments and directives.

NotesDominoQuery – Find the needle in the haystack (LS)

Think about a big database with lots of documents in it and you want to find only one particular document. You can do that with FTSearch, or you can use a with some formula.

As of V10.0.x, you also have DQL and the new NotesDominoQuery class.
The class is available in Lotsscript and Java.
I want to demonstrate in this sample, how you can find the needle in the haystack with DQL in Lotusscript.

My database has about 12.500.000 Documents. It is one of our customers database at midpoints. The amount of documents was created by accident. Some call it a bug. Anyway, the database is a good playground.

The code is typical for a LS developer. It initiates objects and stuff, assigns variables like our dqlTerm (line 6 see the similarity to the @formula, you would probably use with ), does a check, if the target database is open (line 9 ) and also has some basic error handling (17, 20-21). We will come to that later.

I am running the sample on the client. As of today , you cannot run a query client / server. I will show in another post, how you can run the query on the server and work with the results on the client. But that is another story.

public Sub foo() 
	Dim session As New NotesSession
	Dim db As NotesDatabase
	Dim col As NotesDocumentCollection
	Dim dqlTerm As String
	dqlTerm = "form = 'frm.rules.device.rule' And rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"
	Set db = session.Getdatabase("","trul-big.nsf", False)
	If db.Isopen Then
		Set dql = db.CreateDominoQuery()
		Dim parse_result As String
		parse_result = dql.parse(dqlTerm)

		If LCase(parse_result) = "success" Then
			Set col = dql.Execute(dqlTerm)
			MsgBox dql.Explain(dqlTerm)
			MsgBox parse_result
		End If
	End If
End Sub

Before you run the code and try to search such a huge amount of data, you need to set some notes.ini variables.


By now, there is no other way to increase the number of documents. There are setters in the NotesDominoQuery class, but those setters are broken. This is a known issue and HCL is working on a solution.

When we now run the code, we will get the following result

It took 51.3 secs to find 110642 documents that use the form “frm.rules.device.rule” and zero to none secs to find 1 document in that resultset.

Would it be faster or slower, if we modify our query to use only the rule_unid field and search over the entire set of documents?

dqlTerm = "rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"

Here is the result

32.2 secs to find the needle in the haystack. Use this kind of single field search, if you are sure that the field “rule_unid” is only used on one form.

But even in the case that the field is used on another form; to filter the resulting NotesDocument collection afterwards is much faster than the AND dqlTerm from the original code.

Maybe, you already have a view in your application where the rule_unid is in a sorted column.

Then we can use a modified dqlTerm to find the document in that view.

Again, let’s change our dqlTerm a little bit

dqlTerm = "'all'.rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"

And here is the result

THAT is pretty cool, isn’t it? 5.6 msecs to find the needle in the haystack.

Can it be even faster? I think no, but let us take another look at the code that does the query.

        Dim parse_result As String
        parse_result = dql.parse(dqlTerm)
        If LCase(parse_result) = "success" Then
            Set col = dql.Execute(dqlTerm)
            MsgBox dql.Explain(dqlTerm)
            MsgBox parse_result
        End If

We can safely remove all of our “Error handling”, that means lines 14-17 and 20-22. Why? Because dql.Excecute(dqlTerm) does the parse before executing the query.

So we end up with the following code

public Sub foo() 
	Dim session As New NotesSession
	Dim db As NotesDatabase
	Dim col As NotesDocumentCollection
	Dim dqlTerm As String
	dqlTerm = "'all'.rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"

	Set db = session.Getdatabase("","trul-big.nsf", False)
	If db.Isopen Then
		Set dql = db.CreateDominoQuery()
		Set col = dql.Execute(dqlTerm)
		MsgBox dql.Explain(dqlTerm)
	End If
End Sub

When we run the code, we get

Keep in mind that the numbers vary a litte bit from run to run.

If we now modify our dqlTerm and add an error, we are shown a nice dialog box explaining the error in detail.

That’s all for today, I hope you find this information useful.

Bye, bye, Watson Workspace

I received the following mail from IBM announcing the End of Marketing for all Watson Workspace offerings and ending the Watson Workspace service on 2/28/19.

Watson Workspace Announcement

Tomorrow, Tuesday 15 Jan 2019, IBM will formally announce the End of Marketing for all Watson Workspace offerings and we anticipate ending the Watson Workspace service on 2/28/19. This includes Watson Workspace Essentials, Plus, and the free offering. It is our intent to ensure that you have a clear understanding of your options regarding the closure of Watson Workspace.
While there is no question that Watson Workspace is innovative and agile, it hasn’t resonated with clients or obtained the traction in the marketplace necessary for IBM to continue forward with the service. Despite our best efforts and enthusiasm for these offerings, our decision to withdraw them aligns to IBM’s investment strategy, focused on delivering solutions that deliver measurable value to our customers and business partners.
IBM has stopped accepting new orders for Watson Workspace products, and we will not be adding any new features to the offerings. We have been working with our licensed customers and business partners to provide options for handling subscriptions and contracts once the end of service announcement is published to provide a smooth transition.
Starting tomorrow there will be a banner placed into the UI of Watson Workspace informing users of the timeline for moving off the service. Mobile customers may find details in the release notes of the mobile app. We will also provide access to a tool that will allow you to download and save your conversations and content from Watson Workspace. Please plan accordingly to capture any content you’d like to retain as we work to sunset the service. This option will only be available for a limited time.
Thank you for your support of Watson Workspace and IBM Collaboration Solutions. Please find additional details and answers to commonly asked questions in the FAQ that will be posted in the Watson Workspace banner tomorrow.

IBM Collaboration Services, Offering Management

NotesHttpRequest LotusScript example

NotesHttpRequest is a new LotusScript class in Notes V10.0.1 used to make HTTP requests to web servers.

The example wraps the get(), post(), put() and deleteResource() methods in a LotusScript class.

I have also put together a small Node.js / Express.js project that can be used as test server for the NotesHttpRequest example.

Class HttpRequestWrapper

	Library 10010.http
	Created Jan 8, 2019 by Ulrich Krause/singultus
Option Public
Option Declare

Const BASE_URL_PORT = ""
Const CLASS_CUSTOMER ="customers"

Const CUSTOMER_5 = |{
					"firstname" : "Ulrich",
					"lastname" : "Krause",
					"age" : 59,
					"id" : 5

					"firstname" : "Heinz Ulrich",
					"lastname" : "Krause",
					"age" : 59,
					"id" : 5
	Class HttpRequestWrapper
Public Class HttpRequestWrapper
	Private m_session As NotesSession
	Private m_url As String
	Private m_class As String
	Private m_json As String
	Private m_httpRequest As NOTESHTTPREQUEST
		Sub New
	Public Sub New()
		Set m_session = New NotesSession
		Set m_httpRequest = me.m_session.CreateHttpRequest()
		m_httpRequest.Preferstrings = True
		m_url = BASE_URL_PORT
		m_class = CLASS_CUSTOMER
	End Sub
		Function getApiVersion
	Public sub getApiVersion()
		m_json = m_httpRequest.Get(BASE_URL_PORT)
		MsgBox m_json
	End Sub
		Sub getAllObj
	Public Sub getAllObj()
		m_json = m_httpRequest.Get(_
		MsgBox m_json
	End Sub
		Sub getObjById
	Public Sub getObjById(id As String)
		m_json = m_httpRequest.Get(_
		MsgBox m_json	
	End Sub
		Sub createObj
	Public Sub createObj()
		m_json = m_httpRequest.Post(_
		MsgBox m_json	
	End Sub
		Sub updateObj
	Public Sub updateObj(id As String)
		m_json = m_httpRequest.Put(_
		MsgBox m_json
	End Sub
		Sub deleteObj
	Public Sub deleteObj(id As String)
		m_json = m_httpRequest.DeleteResource(_
		MsgBox m_json
	End Sub
End Class

Public Function removeCRLF(json As String) As String
	removeCRLF = Replace(Replace(json, Chr(13), ""),Chr(10),"")
End Function

Sample usage:

Use "10010.http"

Sub Click(Source As Button)
	Dim httpRequestWrapper As New HttpRequestWrapper()

	Call httpRequestWrapper.getApiVersion()
	Call httpRequestWrapper.createObj()
	Call httpRequestWrapper.deleteObj("3")
	Call httpRequestWrapper.getAllObj()
	Call httpRequestWrapper.updateObj("5")
	Call httpRequestWrapper.getObjById("5")
End Sub

Starting Express.js applications with PM2

I recently ran into an issue with PM2. I have created a “helloworld” applications using Express.js.

[root@nodejs projects]# express helloworld
warning: the default view engine will not be jade in future releases
warning: use --view=jade' or--help' for additional options
create : helloworld/
create : helloworld/public/
create : helloworld/public/javascripts/
create : helloworld/public/images/
create : helloworld/public/stylesheets/
create : helloworld/public/stylesheets/style.css
create : helloworld/routes/
create : helloworld/routes/index.js
create : helloworld/routes/users.js
create : helloworld/views/
create : helloworld/views/error.jade
create : helloworld/views/index.jade
create : helloworld/views/layout.jade
create : helloworld/app.js
create : helloworld/package.json
create : helloworld/bin/
create : helloworld/bin/www
change directory:
$ cd helloworld
install dependencies:
$ npm install
run the app:
$ DEBUG=helloworld:* npm start

The application started without issues with npm start.

Next I started the application with PM2

[root@nodejs helloworld]# pm2 start app.js

PM2 displayed the application as “online”, but it constantly restarted and was not accessible.

I found the solution here

[root@nodejs helloworld]# pm2 start bin/www

did the trick for me.

nginx + node.js + CentOS 7 = 502 Bad Gateway

I have setup a new Node.js / Express development environment on a CentOS 7 VM. I ‘ll describe the details in another post later.

To test my setp, I created a new Express application “helloworld”. The application listens on port 3000 and I was able to connect to the application using a browser.

Next, I configured NGINX as reverse proxy to use port 80 to access the helloworld application.

But I got an error

I checked the logs

[root@nodejs ~]# cat /var/log/audit/audit.log | grep nginx | grep denied

and got

type=AVC msg=audit(1546783734.750:239): avc:  denied  { name_connect } for  pid=11084 comm="nginx" dest=3000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:ntop_port_t:s0 tclass=tcp_socket permissive=0

My best guess was SELinux.I checked, if SELinux was enabled.

[root@nodejs ~]# sestatus

SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Max kernel policy version: 31

Next I checked the settings for httpd.

[root@nodejs ~]# getsebool -a | grep httpd
httpd_anon_write --> off
httpd_builtin_scripting --> on
httpd_can_check_spam --> off
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
httpd_can_connect_mythtv --> off
httpd_can_connect_zabbix --> off
httpd_can_network_connect --> off
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
httpd_can_sendmail --> off

So, httpd_can_network_connect was set to “Off”. This blocks the connection from the reverse proxy to the node.js application. As a result, you get the 502 Bad gateway error.

To enable the setting, execute the following command from the shell.

[root@nodejs ~]# setsebool -P httpd_can_network_connect on

You do not need to reboot the machine or SELinux.

ESXi 6.7 Update Failed

I just tried to update my ESXi 6.7 host from ESXi-6.7.0-20181002001-standard (Build 10302608) to ESXi-6.7.0-20181104001-standard (Build 10764712).

Find a list for all available updates and patches here.

The update process in general is straight forward. SSH into your ESXi host and execute the following commands.

esxcli network firewall ruleset set -e true -r httpClient
esxcli software profile update -p ESXi-6.7.0-20181104001-standard -d
esxcli network firewall ruleset set -e false -r httpClient

(replace the highlighted version with the version you want to upgrade to )

This time, the update failed.

Failed to setup upgrade using esx-update VIB: (None, "Failed to mount tardisk /tmp/esx-update-2123405/esxupdt-2123405 in ramdisk esx-update-2123405: [Errno 1] Operation not permitted: '/tardisks.noauto/esxupdt-2123405'")
vibs = ['VMware_bootbank_esx-update_6.7.0-1.31.10764712']
Please refer to the log file for more details.

Digging into the logs, I found the following clues

cpu1:2099635)ALERT: VisorFSTar: 1655: Unauthorized attempt to mount a tardisk
cpu1:2099635)VisorFSTar: 2062: Access denied by vmkernel access control policy prevented creating tardisk

esxupdate: 2099635: root: ERROR: File "/build/mts/release/bora-10302608/bora/build/esx/release/vmvisor/sys-boot/lib64/python3.5/", line 544, in move
esxupdate: 2099635: root: ERROR: PermissionError: [Errno 1] Operation not permitted: '/tmp/esx-update-2123405 /esxupdt-2123405' -> '/tardisks.noauto/esxupdt-2123405 '

By the way, the esxupdate.log is in HEX format for some reason.

You can either use a HEX-Editor to decode the file, or open it in Visual Studio Code.

You’ll get a warning; just click on “Do you want to open it anyway?

After a couple of try and error, I was able to get the updated VIBs using

[root@esxi:~] esxcli software vib update --depot=
Installation Result
Message: The update completed successfully, but the system needs to be rebooted for the changes to be effective.
Reboot Required: true
VIBs Installed: VMware_bootbank_esx-base_6.7.0-1.31.10764712, VMware_bootbank_esx-ui_1.31.0-10201673, VMware_bootbank_esx-update_6.7.0-1.31.10764712, VMware_bootbank_sata-ahci_3.0-28vmw.600.3.107.10474991, VMware_bootbank_vsan_6.7.0-1.31.10720746, VMware_bootbank_vsanhealth_6.7.0-1.31.10720754
VIBs Removed: VMW_bootbank_sata-ahci_3.0-26vmw.670.0.0.8169922, VMware_bootbank_esx-base_6.7.0-1.28.10302608, VMware_bootbank_esx-ui_1.30.0-9946814, VMware_bootbank_esx-update_6.7.0-1.28.10302608, VMware_bootbank_vsan_6.7.0-1.28.10290435, VMware_bootbank_vsanhealth_6.7.0-1.28.10290721
VIBs Skipped: VMW_bootbank_ata-libata-92_3.00.9.2-16vmw.670.0.0.8169922, VMW_bootbank_ata-pata-amd_0.3.10-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-atiixp_0.4.6-4vmw.670.0.0.8169922, VMW_bootbank_ata-pata-cmd64x_0.2.5-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-hpt3x2n_0.3.4-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-pdc2027x_1.0-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-serverworks_0.4.3-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-sil680_0.4.8-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-via_0.3.3-2vmw.670.0.0.8169922, VMW_bootbank_block-cciss_3.6.14-10vmw.670.0.0.8169922, VMW_bootbank_bnxtnet_20.6.101.7-11vmw.670.0.0.8169922, VMW_bootbank_bnxtroce_20.6.101.0-20vmw.670.1.28.10302608, VMW_bootbank_brcmfcoe_11.4.1078.5-11vmw.670.1.28.10302608, VMW_bootbank_char-random_1.0-3vmw.670.0.0.8169922, VMW_bootbank_ehci-ehci-hcd_1.0-4vmw.670.0.0.8169922, VMW_bootbank_elxiscsi_11.4.1174.0-2vmw.670.0.0.8169922, VMW_bootbank_elxnet_11.4.1095.0-5vmw.670.1.28.10302608, VMW_bootbank_hid-hid_1.0-3vmw.670.0.0.8169922, VMW_bootbank_i40en_1.3.1-22vmw.670.1.28.10302608, VMW_bootbank_iavmd_1.2.0.1011-2vmw.670.0.0.8169922, VMW_bootbank_igbn_0.1.0.0-15vmw.670.0.0.8169922, VMW_bootbank_ima-qla4xxx_2.02.18-1vmw.670.0.0.8169922, VMW_bootbank_ipmi-ipmi-devintf_39.1-5vmw.670.1.28.10302608, VMW_bootbank_ipmi-ipmi-msghandler_39.1-5vmw.670.1.28.10302608, VMW_bootbank_ipmi-ipmi-si-drv_39.1-5vmw.670.1.28.10302608, VMW_bootbank_iser_1.0.0.0-1vmw.670.1.28.10302608, VMW_bootbank_ixgben_1.4.1-16vmw.670.1.28.10302608, VMW_bootbank_lpfc_11.4.33.3-11vmw.670.1.28.10302608, VMW_bootbank_lpnic_11.4.59.0-1vmw.670.0.0.8169922, VMW_bootbank_lsi-mr3_7.702.13.00-5vmw.670.1.28.10302608, VMW_bootbank_lsi-msgpt2_20.00.04.00-5vmw.670.1.28.10302608, VMW_bootbank_lsi-msgpt35_03.00.01.00-12vmw.670.1.28.10302608, VMW_bootbank_lsi-msgpt3_16.00.01.00-3vmw.670.1.28.10302608, VMW_bootbank_misc-cnic-register_1.78.75.v60.7-1vmw.670.0.0.8169922, VMW_bootbank_misc-drivers_6.7.0-0.0.8169922, VMW_bootbank_mtip32xx-native_3.9.8-1vmw.670.1.28.10302608, VMW_bootbank_ne1000_0.8.4-1vmw.670.1.28.10302608, VMW_bootbank_nenic_1.0.21.0-1vmw.670.1.28.10302608, VMW_bootbank_net-bnx2_2.2.4f.v60.10-2vmw.670.0.0.8169922, VMW_bootbank_net-bnx2x_1.78.80.v60.12-2vmw.670.0.0.8169922, VMW_bootbank_net-cdc-ether_1.0-3vmw.670.0.0.8169922, VMW_bootbank_net-cnic_1.78.76.v60.13-2vmw.670.0.0.8169922, VMW_bootbank_net-e1000_8.0.3.1-5vmw.670.0.0.8169922, VMW_bootbank_net-e1000e_3.2.2.1-2vmw.670.0.0.8169922, VMW_bootbank_net-enic_2.1.2.38-2vmw.670.0.0.8169922, VMW_bootbank_net-fcoe_1., VMW_bootbank_net-forcedeth_0.61-2vmw.670.0.0.8169922, VMW_bootbank_net-igb_5., VMW_bootbank_net-ixgbe_3., VMW_bootbank_net-libfcoe-92_1., VMW_bootbank_net-mlx4-core_1.9.7.0-1vmw.670.0.0.8169922, VMW_bootbank_net-mlx4-en_1.9.7.0-1vmw.670.0.0.8169922, VMW_bootbank_net-nx-nic_5.0.621-5vmw.670.0.0.8169922, VMW_bootbank_net-tg3_3.131d.v60.4-2vmw.670.0.0.8169922, VMW_bootbank_net-usbnet_1.0-3vmw.670.0.0.8169922, VMW_bootbank_net-vmxnet3_1.1.3.0-3vmw.670.0.0.8169922, VMW_bootbank_nfnic_4.0.0.14-0vmw.670.1.28.10302608, VMW_bootbank_nhpsa_2.0.22-3vmw.670.1.28.10302608, VMW_bootbank_nmlx4-core_3.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx4-en_3.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx4-rdma_3.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx5-core_4.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx5-rdma_4.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_ntg3_4.1.3.2-1vmw.670.1.28.10302608, VMW_bootbank_nvme_1.2.2.17-1vmw.670.1.28.10302608, VMW_bootbank_nvmxnet3-ens_2.0.0.21-1vmw.670.0.0.8169922, VMW_bootbank_nvmxnet3_2.0.0.29-1vmw.670.1.28.10302608, VMW_bootbank_ohci-usb-ohci_1.0-3vmw.670.0.0.8169922, VMW_bootbank_pvscsi_0.1-2vmw.670.0.0.8169922, VMW_bootbank_qcnic_1., VMW_bootbank_qedentv_2.0.6.4-10vmw.670.1.28.10302608, VMW_bootbank_qfle3_1.0.50.11-9vmw.670.0.0.8169922, VMW_bootbank_qfle3f_1., VMW_bootbank_qfle3i_1., VMW_bootbank_qflge_1.1.0.11-1vmw.670.0.0.8169922, VMW_bootbank_sata-ata-piix_2.12-10vmw.670.0.0.8169922, VMW_bootbank_sata-sata-nv_3.5-4vmw.670.0.0.8169922, VMW_bootbank_sata-sata-promise_2.12-3vmw.670.0.0.8169922, VMW_bootbank_sata-sata-sil24_1.1-1vmw.670.0.0.8169922, VMW_bootbank_sata-sata-sil_2.3-4vmw.670.0.0.8169922, VMW_bootbank_sata-sata-svw_2.3-3vmw.670.0.0.8169922, VMW_bootbank_scsi-aacraid_1.1.5.1-9vmw.670.0.0.8169922, VMW_bootbank_scsi-adp94xx_1.0.8.12-6vmw.670.0.0.8169922, VMW_bootbank_scsi-aic79xx_3.1-6vmw.670.0.0.8169922, VMW_bootbank_scsi-bnx2fc_1.78.78.v60.8-1vmw.670.0.0.8169922, VMW_bootbank_scsi-bnx2i_2.78.76.v60.8-1vmw.670.0.0.8169922, VMW_bootbank_scsi-fnic_1.5.0.45-3vmw.670.0.0.8169922, VMW_bootbank_scsi-hpsa_6.0.0.84-3vmw.670.0.0.8169922, VMW_bootbank_scsi-ips_7.12.05-4vmw.670.0.0.8169922, VMW_bootbank_scsi-iscsi-linux-92_1.0.0.2-3vmw.670.0.0.8169922, VMW_bootbank_scsi-libfc-92_1., VMW_bootbank_scsi-megaraid-mbox_2.20.5.1-6vmw.670.0.0.8169922, VMW_bootbank_scsi-megaraid-sas_6.603.55.00-2vmw.670.0.0.8169922, VMW_bootbank_scsi-megaraid2_2.00.4-9vmw.670.0.0.8169922, VMW_bootbank_scsi-mpt2sas_19.00.00.00-2vmw.670.0.0.8169922, VMW_bootbank_scsi-mptsas_4.23.01.00-10vmw.670.0.0.8169922, VMW_bootbank_scsi-mptspi_4.23.01.00-10vmw.670.0.0.8169922, VMW_bootbank_scsi-qla4xxx_5.01.03.2-7vmw.670.0.0.8169922, VMW_bootbank_shim-iscsi-linux-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-iscsi-linux-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libata-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libata-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfc-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfc-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfcoe-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfcoe-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-vmklinux-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-vmklinux-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-vmklinux-9-2-3-0_6.7.0-0.0.8169922, VMW_bootbank_smartpqi_1.0.1.553-12vmw.670.1.28.10302608, VMW_bootbank_uhci-usb-uhci_1.0-3vmw.670.0.0.8169922, VMW_bootbank_usb-storage-usb-storage_1.0-3vmw.670.0.0.8169922, VMW_bootbank_usbcore-usb_1.0-3vmw.670.0.0.8169922, VMW_bootbank_vmkata_0.1-1vmw.670.0.0.8169922, VMW_bootbank_vmkfcoe_1.0.0.1-1vmw.670.1.28.10302608, VMW_bootbank_vmkplexer-vmkplexer_6.7.0-0.0.8169922, VMW_bootbank_vmkusb_0.1-1vmw.670.1.28.10302608, VMW_bootbank_vmw-ahci_1.2.3-1vmw.670.1.28.10302608, VMW_bootbank_xhci-xhci_1.0-3vmw.670.0.0.8169922, VMware_bootbank_cpu-microcode_6.7.0-1.28.10302608, VMware_bootbank_elx-esx-libelxima.so_11.4.1184.0-0.0.8169922, VMware_bootbank_esx-dvfilter-generic-fastpath_6.7.0-0.0.8169922, VMware_bootbank_esx-xserver_6.7.0-0.0.8169922, VMware_bootbank_lsu-hp-hpsa-plugin_2.0.0-16vmw.670.1.28.10302608, VMware_bootbank_lsu-intel-vmd-plugin_1.0.0-2vmw.670.1.28.10302608, VMware_bootbank_lsu-lsi-lsi-mr3-plugin_1.0.0-13vmw.670.1.28.10302608, VMware_bootbank_lsu-lsi-lsi-msgpt3-plugin_1.0.0-8vmw.670.0.0.8169922, VMware_bootbank_lsu-lsi-megaraid-sas-plugin_1.0.0-9vmw.670.0.0.8169922, VMware_bootbank_lsu-lsi-mpt2sas-plugin_2.0.0-7vmw.670.0.0.8169922, VMware_bootbank_lsu-smartpqi-plugin_1.0.0-3vmw.670.1.28.10302608, VMware_bootbank_native-misc-drivers_6.7.0-0.0.8169922, VMware_bootbank_qlnativefc_3.0.1.0-5vmw.670.0.0.8169922, VMware_bootbank_rste_2.0.2.0088-7vmw.670.0.0.8169922, VMware_bootbank_vmware-esx-esxcli-nvme-plugin_1.2.0.34-1.28.10302608, VMware_locker_tools-light_10.3.2.9925305-10176879

[HomeLab] – Copy VM from one ESXi host to another

I recently decided that it is time to setup a new homelab. The old server is about 10 yrs old. The hardware does not allow any upgrade in CPU and Ram. VMWare ESXi was version 6.5, but I could not upgrade to version 6.7 because the network card was not in the list of supported NICs and so the upgrade failed. Last, but not least, the power consumption was at 200W.

The new HomeLab has the following components

It took less than 30 minutes to assemble the NUC and install VMWare ESXi 6.7. ( + 15 minutes to drive to the local hardware store to grab an USB keyboard once I realized that I would need one for the setup )

Today, I migrated the existing VMs from the old host to the new one.

ESXi does not include VMotion. VMotion costs a lot of money.
I had read some articles which claimed to be best practice. But to be honest, using Veeam or SCP are not, what I consider “best” practice. I tried SCP, but it was so slooooow. Even a 50GB VM was estimated 11 hours to copy. And I have 30 VMs from just a couple of MB to 100GB.

I searched for a better solution. And I finally found it. VMware vCenter Converter Standalone Client.

You simply choose the “source” ESXi instance and select the VM to copy. Next you select the “target” ESXi. You can also choose, if the copy will be automatically updated to the target VM version.

It took only 30 minutes to copy a 50GB VM. Another 100GB VM was copied in 20 minutes.

The whole migration was done in only 5 hours. Not bad, isn’t it.

NotesJsonNavigator.getElementByPointer example

Sample code:

Const white = |{"color": "white", "category": "value","code": {"rgba": [0,0,0,1],"hex": "#FFF"}}|

Public Sub testJsonNavGetElementByPointer()
	Dim session As New NotesSession
	Dim jsnav As NotesJSONNavigator 
	Set jsnav = session.CreateJSONNavigator(removeCRLF(white)) 
	'// returns "value"
	Set el = jsnav.Getelementbypointer("/category")
	MsgBox "category: " + el.Value
	'// returns "#FFF"
	Set el = jsnav.Getelementbypointer("/code/hex")
	MsgBox "hex: " + el.Value
	'// returns the 4th element in the rgba array  "1"
	Set el = jsnav.Getelementbypointer("/code/rgba/3")
	MsgBox "code/rgba/3: " + el.Value
End Sub

General information about JSON pointer:

NotesJsonNavigator.getNthElement does not obey boundries

I stumbled upon an issue with NotesJsonNavigator getNthElement(index) method.

It looks like there is no boundry check, which leads to some inconsitent behaviour and unpredictable results.

Here is the code that I used in my test.

	Sub testJsonNavGetNthElement
Public Sub testJsonNavGetNthElement
	Dim s As New NotesSession 
	Dim jsnav As NotesJSONNavigator
	Dim el As NotesJSONElement
	Set jsnav = s.CreateJSONNavigator(|{ "element1" : "value 1", "element2" : "value 2", "element3 ": "value 3" }|)	
	Set el = jsnav.GetNthElement(0)
	Set el = jsnav.GetNthElement(1)
	Set el = jsnav.GetNthElement(2)
	Set el = jsnav.GetNthElement(3)
	Set el = jsnav.GetNthElement(1000)
End Sub

The issue occurs with index < 1 and > upper bound of the array.

Index 0 AND index 1 both return the same value; “value 1“.
Index 1000 returns NULL or nothing.

This is not the expected behaviour. At least I would expect some “out of bounds” error.
Also, it is not clear for me what the base for the index is. Do we start counting at 0 or do we start with 1 ?

According to the documentation, the index is 1-based.

NotesJsonNavigator, NotesJsonElement, NotesJsonArray, NotesJsonObject example

NotesJsonNavigator, NotesJsonElement, NotesJsonArray, NotesJsonObject are new classes in Domino Designer as of Notes V10.0.1. They are not yet documented in the Domino Designer Help, but you can find online documentation following the above links.

The documentation also contains some basic samples.

In this article, I will demonstrate how to use the classes beyond the basic examples.
I’ll also show, how you can use the new NotesHttpRequest class to connect to a server and read and parse view data as JSON.

The first thing you need to know is that the NotesJsonNavigator class does not like CRLF.
When you try to create a new NotesJsonNavigator from the following JSON data

Const colors = |{
  "colors": [
      "color": "black",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [255,255,255,1],
        "hex": "#000"
      "color": "white",
      "category": "value",
      "code": {
        "rgba": [0,0,0,1],
        "hex": "#FFF"
      "color": "red",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [255,0,0,1],
        "hex": "#FF0"
      "color": "blue",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [0,0,255,1],
        "hex": "#00F"
      "color": "yellow",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [255,255,0,1],
        "hex": "#FF0"
      "color": "green",
      "category": "hue",
      "type": "secondary",
      "code": {
        "rgba": [0,255,0,1],
        "hex": "#0F0"

you will get an error.

You can remove all CRLF from the data using this helper function.

Public Function removeCRLF(json As String) As String
	removeCRLF = Replace(Replace(json, Chr(13), ""),Chr(10),"")
End Function

Let us create the NotesJsonNavigator from NotesSession first.

Dim session As New NotesSession 
Dim jsnav As NotesJSONNavigator 
Dim json As String
json = removeCRLF(colors)
Set jsnav = session.CreateJSONNavigator(json)

Now we can count how many different colors we would find in our JSON object

Set el = jsnav.GetFirstElement()

Set arr = el.value
MsgBox "Elements count: " + CStr(arr.size)

And finally, we want to get the value for the second color in the JSON object.

Although there is a GetNthElement (index) method, this method seems to be buggy or not yet fully implemented. GetNthElement(index) will always return the first element from the JSON object.

So we will use GetFirstElement and GetNextElement to navigate thru the object.

'Set el = arr.Getnthelement(2)
Set el = arr.GetFirstElement()
Set el = arr.GetNextElement()

Set obj = el.Value
Set el = obj.Getelementbyname("color")
MsgBox "color: " + CStr(el.Value)

The next sample creates a NotesHttpRequest and gets JSON from a view in a Notes application. Then we retrieve the UNID and NoteId from the data returned using the new NotesJson… classes.

	Library 10010.http
	Created Jan 1, 2019 by Ulrich Krause/singultus
	Description: Comments for Library
Option Public
Option Declare

Public Sub httpGet
	Dim Session As New NotesSession        
	Dim ret As String
	Dim URL As String

	Dim user As String
	Dim password As String
	Dim httpReq As NotesHTTPRequest
	Set httpReq = session.CreateHttpRequest()
	httpReq.Preferstrings = True
	user = ""
	password = "pAssw0rd"
	URL = "https://yourserver/names.nsf/($certifiers)?readviewentries&outputformat=JSON"

	Call httpReq.Setheaderfield("Authorization", "Basic " + EncodeBase64 (user + ":" + password))

	Dim json As string
	json = httpReq.Get(URL)

	Dim jsnav As NotesJSONNavigator 
	Set jsnav = session.CreateJSONNavigator(removeCRLF(json))


	Set el = jsnav.GetElementByName("viewentry")

	Set arr = el.value
	Set el = arr.GetFirstElement()
	Set el = arr.GetNextElement()
	Set obj = el.Value
	Set el = obj.Getelementbyname("@unid")

	MsgBox "unid: " + CStr(el.Value)
	Set el = obj.Getelementbyname("@noteid")

	MsgBox "noteid: " + CStr(el.Value)

End Sub

Private Function removeCRLF(json As String) As String
	removeCRLF = Replace(Replace(json, Chr(13), ""),Chr(10),"")
End Function

Private Function EncodeBase64 (StrIn As String) As String
	Dim session As New NotesSession
	Dim stream As NotesStream
	Dim db As NotesDatabase
	Dim doc As NotesDocument
	Dim body As NotesMIMEEntity
	Set stream = session.CreateStream
	Call stream.WriteText (StrIn)
	Set db = session.CurrentDatabase
	Set doc = db.CreateDocument
	Set body  = doc.CreateMIMEEntity
	Call body.SetContentFromText (stream, "", ENC_NONE)
	Call body.EncodeContent (ENC_BASE64)
	EncodeBase64 = body.ContentAsText
	Call stream.Close
	Set doc = Nothing
End Function

Finding And Fixing Node.js Memory Leaks: A Practical Guide

Fixing memory leaks may not be not the shiniest skill on a CV, but when things go wrong on production, it’s better to be prepared!
After reading this article, you’ll be able to monitor, understand, and debug the memory consumption of a Node.js application.

Kévin Maschtaler

In addition to this very good article , here are a couple of tipps how to enable remote debugging.

node –inspect= yourapp.js

This will bind the debugger to a different IP address:port. Otherwise only LOCALHOST will be available

If you are using PM2

pm2 start –node-args=”–inspect=″ yourapp.js

Then open Chrome browser and navigate to chrome://inspect

Check “Discover network targets” and click “Configure”

Your application is ready for inspect

NotesDominoQuery sample

I have put together a small sample to demonstrate how to use NotesDominoQuery from LotusScript.

I created a new Class DQLWrapper. A little bit over the top, I know.

	Library 10010.dql
	Created Dec 30, 2018 by Ulrich Krause/singultus
Option Declare

	Class DqlWrapper
Public Class DqlWrapper
	m_query As String
	m_session As NotesSession
	m_db As NotesDatabase
	m_ndq As NotesDominoQuery
		Sub New
	Public Sub New(strDbFilePath As String)
		Set me.m_session = New NotesSession
		Set me.m_db = me.m_session.Getdatabase("",strDbFilePath, False)
		If ( me.m_db.Isopen ) then
			Set me.m_ndq = me.m_db.Createdominoquery()
			' // do some error handling
		End if
	End Sub

		Public function executeQuery()
	Public function executeQuery() As NotesDocumentCollection
		If ( me.m_query <> "" ) then
			Set executeQuery = me.m_ndq.Execute(me.m_query)
			Set executeQuery = nothing
		End If
	End Function

		Public Function explainQuery()
	Public Function explainQuery() As String
		If ( me.m_query <> "" ) Then
			explainQuery = me.m_ndq.Explain(me.m_query)
			explainQuery = ""
		End If
	End Function
		Public Function explainQuery()
	Public Function parseQuery() As String
		If ( me.m_query <> "" ) Then
			parseQuery = me.m_ndq.parse(me.m_query)
			parseQuery = ""
		End If
	End Function	

		Property query
	Public Property Set query As String
		me.m_query = query
	End property
End Class

The query itself is executed from an agent that runs on the server. At the moment it is not possible to run a query client/ server.

Here is the code for the agent

	Agent dql.execute
	Created Dec 30, 2018 by Ulrich Krause/singultus
Option Public
Option Declare
Use "10010.dql"

Sub Initialize
	Dim query As String
	Dim col As  NotesDocumentCollection
	query = "firstname = 'Ulrich' And lastname = 'Krause'"
	Dim dql As New DQlWrapper("names.nsf")
	dql.query = query
	If (  LCase(dql.parseQuery()) ="success" ) Then
		Set col = dql.executeQuery()
		MsgBox "QRY returns # docs: " + CStr(col.count)
		If ( col.count > 0 ) then
			Dim doc As NotesDocument
			Set doc = col.Getfirstdocument()
			MsgBox "UNID of first doc: " + doc.Universalid
		End if
		MsgBox dql.explainQuery()
	End If
End Sub

You can now start the agent from the server console. You will get the number of documents for this query and the UNID of the first document found.

te amgr run "ec11.nsf" 'dql.execute'
[0DFC:001F-0FFC] 30.12.2018 13:49:10 AMgr: Start executing agent 'dql.execute' in 'ec11.nsf'
[0DFC:001F-0FFC] 30.12.2018 13:49:10 Agent Manager: Agent message: QRY returns # docs: 1
[0DFC:001F-0FFC] 30.12.2018 13:49:10 Agent Manager: Agent message: UNID of first doc: D8436D0F4E546BA3C12573FE0070AE88
[0DFC:001F-0FFC] 30.12.2018 13:49:10 AMgr: Agent 'dql.execute' in 'ec11.nsf' completed execution

If your query contains errors / is not understandable, you will see an output similar like this on your console

[0DFC:0020-11D0] 30.12.2018 13:59:45   Agent Manager: Agent 'dql.execute' error: Domino Query execution error:   Query is not understandable -  syntax error     - processing or expecting operator (=, <, <= …) token syntax    (Call hint: OSCalls::OSLocalAllc, Core call 
0) firstname = 'Ulrich' And lastname IS 'Krause' …………………………….^……….. ****
[0DFC:0020-11D0] 30.12.2018 13:59:45 AMgr: Agent 'dql.execute' in 'ec11.nsf' completed execution

Start node.js application using systemd on RHEL 7

In a test or development environment we can start a node.js application with npm start . But this is not, what we want to do in production.

We need to start the application as soon as the server is up and running.

Here is a quick and easy way to achieve this goal.

Create a new file in /etc/systemd/system ( domino-db.service for example )

Description=domino-node-list sample



Modify ExecStart and WorkingDirectory to point to the correct path in your installation.

Save domino-db.service and set execution rights to 744.

Check app.js for proper execution right (744).

Edit app.js and add

#!/usr/bin/env node

as the first line in the code.

Enable the service

systemctl enable domino-db

Created symlink from /etc/systemd/system/ to /etc/systemd/system/domino-db.service.

Check with

systemctl status domino-db

● domino-db.service - domino-node-list sample
   Loaded: loaded (/etc/systemd/system/domino-db.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2018-12-30 10:24:04 CET; 4min 15s ago
 Main PID: 7163 (node)
    Tasks: 7
   CGroup: /system.slice/domino-db.service
           └─7163 node /git/domino-node-list/app.js

Dec 30 10:24:04 systemd[1]: Started domino-node-list sample.
Dec 30 10:24:04 systemd[1]: Starting domino-node-list sample...
Dec 30 10:24:05 app.js[7163]: Example app listening at http://:::3000

You can now start and stop the service at any time using

systemctl start domino-db
systemctl stop domino-db

[Solved] – PROTON_SSL=1

This is a follow up to my recent post

Due to my misunderstanding and also a lack in documentation, I was not able to get PROTON with PROTON_SSL=1 and PROTON_AUTHENTICATION=client_certs running.

But thanks to our great community and a little help from Jan Krejcárek (SUTOL), I was finally able to solve the puzzle.

I must admit that I am not an expert in this field. So please bare with me if things are obvious for you. They are (not yet) for me.

My understanding of using TLS/SSL with PROTON was that I could use my existing Let’s Encrypt certificate to communicate via HTTPS instead of HTTP with my server URL.

But this is not the case. TLS/SSL encryption only enables a secured communication between the domino.db module and the PROTON addin sitting on the Domino server.

Here is an image.

The second thing that I got wrong was the fact that the existing certificate and it’s CA would be enough to enable the secured communication. In my first test, I had set PROTON_AUTHENTICATION=anonymous.

So why bother with client certificates if they are not needed at this point?

I followed Jan’s advice to try with a new generated self-signed certificate. The AppDevPack contains shell scripts to create some sample certs and also lets you create a keyring file that can be used with PROTON.

I have modified the file a bit because it complained about missing environment variables.

# added UKR, 12/2018
export NOTESDATA=/local/notesdata
export NOTESBIN=/opt/ibm/domino/notes/10000000/linux

I then created the certs and keyring and copied them to the proper location.

The original source code has used

// proton config
const serverConfig = {
    "hostName": "",
    "connection": {
        "port": 3002

to define the serverConfig. This must be enhanced, if you are going the secure way.

I created a new module using Jan’s code

const fs = require('fs');
const path = require('path');

 * Internal functions that reads a content of a file to a buffer.
 * @param {string} fileName 
const readFile = fileName => {
        return fs.readFileSync(path.resolve(fileName));

 * (c) Jan Krejcárek
 * Creates an object in a structure required for the DominoDB module
 * to initialize o connection to the Domino server.
 * @param {string} serverHostName Host name of the Domino server
 * @param {string} port TCP port number where a Proton task listens for connection requests
 * @param {string} rootCertificatePath Path to the certificate used to establish a TLS connection
 * @param {string} clientCertificatePath Path to the application certificate used to authenticate the application
 * @param {string} clienKeyPath Path to the private key of the application
const config = (serverHostName, port, rootCertificatePath, clientCertificatePath, clienKeyPath) => {
    const rootCertificate = readFile(rootCertificatePath);
    const clientCertificate = readFile(clientCertificatePath);
    const clientKey = readFile(clienKeyPath);
    const serverConfig = {
            hostName: serverHostName,
            connection: {
                    port: port,
                    secure: true
            credentials: {

    return serverConfig;

module.exports = config;

All parts in the credentials section are mandatory. Even if you use PROTON_AUTHENTICATION=anonymous with PROTON_SSL=1, you must have certificates and keys for the client as well.

And we can now use this module in app.js

const protonConfig = require('./protonConfigSSL.js');
const serverConfig = protonConfig("", "3002", "./certs/proton-self/ca.crt", "./certs/proton-self/app1.crt", "./certs/proton-self/app1.key");

My PROTON configuration is

[029971:000009-00007F78D8293700] PROTON_AUTHENTICATION=client_cert
[029971:000009-00007F78D8293700] PROTON_KEYFILE=proton-self.kyr
[029971:000009-00007F78D8293700] PROTON_LISTEN_PORT=3002
[029971:000009-00007F78D8293700] PROTON_SSL=1
[029971:000009-00007F78D8293700] PROTON_TRACE_REQUEST=0
[029971:000009-00007F78D8293700] PROTON_TRACE_SESSION=0

And after restarting PROTON ( restart task proton ) and starting my application ( npm start ), I was able to open hp.nsf in the browser.

I also found an interesting article by Sven Hasselbach about how to protect PROTON keys.

I hope that this will help others starting with this stuff to save some time.

Recommended reading:

Heiko Voigt: DominoDB and a big NO-NO !

Sven Hasselbach ( response to Heiko’s post ): DominoDB and a big NO-NO?

Problem using PROTON with PROTON_SSL=1 enabled

By default, PROTON supports anonymous access and an insecure connection. This is a good starting point, but if you want to do something more productive, you should at least have PROTON_SSL set to 1 and a valid certificate at hand.

You can also use a self signed certificate, but most, if not all browsers will reject it. I am using a certificate issued by Lets Encrypt. This is the same certificate that I am using on my Domino server to secure HTTPS connections.

My sample node application uses the excellent sample written by Oliver Busse from
I have changed package.json to use the latest domino-db module

"name": "domino-node-list",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
"author": "",
"license": "ISC",
"dependencies": {
"@domino/domino-db": "file:vendor/domino-domino-db-1.1.0.tgz",
"ejs": "^2.6.1",
"express": "^4.16.3"

I also left app.js unchanged

// domino-db
const {
} = require('@domino/domino-db');

// proton config
const serverConfig = {
"hostName": "",
"connection": {
"port": 3002

// domino nsf
const databaseConfig = {
"filePath": "hp.nsf"

I created hp.nsf according to the NSF source description in the reporitory.

Needed ports on the host are open

[root@serv02 domino-node-list]# firewall-cmd --list-ports
22/tcp 8585/tcp 1352/tcp 3001/tcp 3002/tcp 3000/tcp 10000/tcp 21/tcp 8443/tcp 443/tcp 80/tcp


First test with


12/28/2018 04:36:56.50 AM PROTON> Listening on, INSECURE
12/28/2018 04:36:56.50 AM PROTON> Server initialized
12/28/2018 04:36:56.50 AM PROTON> Server allows Anonymous access only.

node app has been started

> domino-node-list@1.0.0 start /git/domino-node-list
> node app.js
Example app listening at http://:::3000

In the browser, I typed

On the server console, I got:

[013713:000006-00007FCB3C40C700] 12/28/2018 04:39:42.88 AM PROTON> Session::init()
[013713:000006-00007FCB3C40C700] Tokens parsed

[013713:000006-00007FCB3C40C700] order = 1, level = 0, token = {form}, delim { } type = IDENTIFIER, opertype = NOT AN OPERATION, booltype = NONE
[013713:000006-00007FCB3C40C700] order = 2, level = 0, token = {=}, delim { } type = OPERATOR, opertype = EQUALITY, booltype = NONE
[013713:000006-00007FCB3C40C700] order = 3, level = 0, token = {'post'}, delim {'} type = QUOTED STRING, opertype = NOT AN OPERATION, booltype = NONE

[013887:000009-00007F93BA436700] Documents scanned = 4
[013887:000009-00007F93BA436700] Count of docs found = 2


Next, I tried with PROTON_SSL=1 ( eknori.kyr uses a valid Lets Encrypt certificate, no client certificates created and configured at this time )

sh con proton*

lo proton
12/28/2018 04:10:14.55 AM PROTON> Build 0.2.2
12/28/2018 04:10:15.69 AM PROTON> Listening on, SSL-ENABLED
12/28/2018 04:10:15.69 AM PROTON> Server initialized
12/28/2018 04:10:15.69 AM PROTON> Server allows Anonymous access only.

sh ta o
HTTP Server Listen for connect requests on TCP Port:80, 443

When I know try to open, I get

Secure Connection Failed
The connection to was interrupted while the page was loading.
The page you are trying to view cannot be shown because the authenticity of the received data could not be verified. Please contact the website owners to inform them of this problem.

Request headers (355 B)
Accept text/html,application/xhtml+xm…plication/xml;q=0.9,/;q=0.8
Accept-Encoding gzip, deflate, br
Accept-Language en-US,de;q=0.7,en;q=0.3
Connection keep-alive
Upgrade-Insecure-Requests 1
User-Agent Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/64.0

I have verified that I can connect to PROTON

[root@serv02 domino-node-list]# openssl s_client -connect
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN =
verify return:1
Certificate chain
0 s:/
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
Server certificate
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
No client certificate CA names sent
Peer signing digest: SHA256
Server Temp Key: ECDH, P-256, 256 bits
SSL handshake has read 3260 bytes and written 415 bytes
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: 49DA29C84DEFE38DAE3B7458F1E59859145DC5C27A27B248E1AC1958175F3BCC
Master-Key: EC83FBCB155BEF83E9450C73D6A56A487DE933FC2D1405F3D95E725D9698962378BC65CA8D683F985415DF02D39A266F
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
0000 - b8 b4 e4 5d 80 ec 53 c5-a5 06 8c 23 19 82 2e f2 …]..S….#….
0010 - 4e 66 a9 3a 5c d6 d6 09-b1 cb 69 b2 2e 16 f9 37 Nf.:.….i….7
0020 - 81 b6 db 13 a3 08 76 e0-59 91 38 12 15 0a 43 9e ……v.Y.8…C.
0030 - da df b2 8c d4 57 00 be-ef 77 30 12 77 4e 08 92 …..W…w0.wN..
0040 - 1c d6 ee ca d2 98 41 79-47 c7 b7 69 1e 93 f4 91 ……AyG..i….
0050 - 80 8e 9a 20 f7 88 30 4d-22 3b 96 37 22 6c 1e bd … ..0M";.7"l..
0060 - ca 98 87 3c 25 4c ca 33-c9 07 a3 45 a3 92 51 9c …<%L.3…E..Q.
0070 - a2 60 a6 fa 3f 5d 8f 6e-30 f9 75 4e 8d e1 17 ea .`..?].n0.uN….
0080 - f2 99 c5 74 fb d4 94 38-9d 29 39 8a 01 82 50 1a …t…8.)9…P.
0090 - e0 a2 af 40 44 75 a6 2d-a1 06 c5 9a 50 7a 3c 2e …@Du.-….Pz<.
Start Time: 1545966761 Timeout : 300 (sec)
Verify return code: 0 (ok)

I also tried a simple I (expected) result is the following server console output

[013816:000007-00007FBB7D375700] 12/28/2018 04:48:24.89 AM PROTON> GET request without QUERY [src/core/ext/filters/http/server/http_server_filter.c:236]
[013816:000007-00007FBB7D375700] 12/28/2018 04:48:24.89 AM PROTON> Invalid entry in accept encoding metadata: ' deflate'. Ignoring. [src/core/lib/surface/call.c:940]
[013816:000007-00007FBB7D375700] 12/28/2018 04:48:24.89 AM PROTON> Invalid entry in accept encoding metadata: ' br'. Ignoring. [src/core/lib/surface/call.c:940]

So, apparently, connection via external server IP address is possible, but there is a problem when the node app tries to communicate to the PROTON addin.
Unfortunately, there are no error messages; neither on the Domino server console nor in the node app.

LotusScript: NotesDominoQuery – first steps

I upgraded my Domino Server and Notes Client from V10 to V10.01 yesterday. V10.01 introduces some new features for developers.

Amongst others, there is a new class in LotusScript; NotesDominoQuery. The class is used to compile, tune, and run Domino Query Language queries.

The class and it’s methods and properties is not (yet) documented in the Domino Designer Help. But you can find the documentation ( and examples ) here.

Also not documented is that you cannot run a query client/server. According to John Curtis this is not possible at this point.

I put together a small agent to execute a simple query on the server names.nsf that should return the number of douments found. When I ran the agent ( tell amgr run “db.nsf” ‘myAgent’, it found the document matching the query but also printed additional lines to the console.

I did some research and found that I have to set DEBUG_GQF_QUERY=0. John Curtis confirmed on Twitter that the variable has to be set.





Install and manage node.js via NVM

NVM is known as Node Version Manager and provides an option for easy installation of Node.js.
You can install a specific Node.js version or multiple Node.js versions on the same system using nvm and use required version for application.

A bash script is available to install nvm on your system.

curl | bash

Reload system environment using this command. It will set the required environment variables to use nvm on the system.

source ~/.bashrc      ## for CentOS/RHEL systems 


nvm ls-remote 

to list available node.js versions.

Now install the node.js version you need to use for running node.js application.

nvm install v8.10.0

nvm lets you install multiple node.js versions. Simply repat the above nvm install with the needed node.js version.

As you have installed multiple node.js versions, You can select the specific version of node.js as default version used by system and load in the environment.

nvm use v8.10.0

If you have multiple node.js applications on your system and want to run each with a specific version of node.js. NVM provides you an option to use node.js version for running any application.

nvm run v8.10.0 app.js
nvm list

will provide a list of installed versions of node.js on your system. To remove any version installed on your system use

nvm remove v7.7.2

Plain simple guide to setup Domino V10 on Red Hat Enterprise Linux Server 7.4

Red Hat Enterprise Linux Server (RHEL) is one of the supported platforms for Domino V10. In this post I will show how to setup a Domino V10 server on RHEL 7.4 in a just a few steps.

Did you know, by joining the free Red Hat Developers program, you can get a no-cost Red Hat Enterprise Linux subscription for developers?

This subscription includes: Red Hat Enterprise Linux Server (all currently supported releases), additional development tools, and numerous add-ons such as resilient storage, scalable file systems, and high-performance networking. The no-cost subscription also includes access to the Red Hat Customer Portal for software updates and thousands of knowledgebase articles.

After you have successfully registered as a Red Hat Enterprise Linux® Developer, you can download the iso image to setup a new RHEL 7.4 installation.
I will not go into the installation details here. I just recommend to select “minimal install”. Remember, we are installing a server. So why would we need all the overhead of a GUI.

When the installation has finished successfully you’ll have a running RHEL server 7.4. Make sure that the server is connected to the internet because we need to install a couple of packages prior to installing Domono V10.

To get updates for your RHEL 7.4 server, you must register the machine with Red Hat first using the credentials that have been used when registering for the no-cost Red Hat Enterprise Linux® Developer subscription.

subscription-manager  register --username my.mailaddress@mydomain.tld --password PaszVV0rd --auto-attach --force

Registering to:
The system has been registered with ID: 7e798124-d48a-4b55-826b-b9c77a398648 

Installed Product Current Status:
Product Name: Red Hat Enterprise Linux Server
Status:       Subscribed

The Domino installer has some dependencies that are not installed during the initial setup of our RHEL 7.4 server.

yum install perl
yum install bc-1.06.95-13.el7.x86_64

To access your server via the TCP/IP, you must open some ports first. Install the firewalld package

yum install firewalld

and then open the needed ports

firewall-cmd --zone=public --add-port=22/tcp --permanent  
firewall-cmd --zone=public --add-port=8585/tcp --permanent
firewall-cmd --zone=public --add-port=1352/tcp --permanent

After that reload your configuration.

firewall-cmd --reload

Next we can install Domino V10. As user root upload DOMINO_SERVER_V10.0_64_BIT_LINUX_.tar to i.e. /tmp and unpack the archive with

cd /tmp
tar xvf DOMINO_SERVER_V10.0_64_BIT_LINUX_.tar

cd linux64/domino

The installation wizard will start

          Initializing Wizard........
          Launching InstallShield Wizard........

Welcome to the InstallShield Wizard for IBM Domino

The InstallShield Wizard will install IBM Domino on your computer.
To continue, choose Next.

IBM Domino


Press 1 for Next, 3 to Cancel or 5 to Redisplay [1] 

Follow the instructions on the screen. Depending on your hardware the installation process will take about 5-10 minutes.

After you install the program files for an IBM® Lotus® Domino® server on a system, you can use either a Microsoft® Windows® client system or another Domino server to run the server setup program remotely. Running the server setup program from a Windows client is easier if the client has Domino Administrator installed — to run the program from a client without Domino Administrator, you need the Java™ runtime environment plus some files from the program directory of an installed Domino server.

Follow the instructions in this technote to configure your Domino V10 server.

Finally, we want to start the Domino V10 server when the OS starts. this can easily be done using the start script by Daniel Nashed. You can get a free copy of the script here.

There will be a newer version of the script soon. Watch Daniel’s blog for updates.

Installation is straight forward; Just follow the instructions in the “Quick Configuration” section of the rc_domino_readme.txt file that comes with the start script.

[root@serv02 ~]# systemctl status domino.service
● domino.service - IBM Domino Server (notes)
   Loaded: loaded (/etc/systemd/system/domino.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2018-11-27 15:48:55 CET; 14h ago
  Process: 1120 ExecStart=/opt/ibm/domino/rc_domino_script start (code=exited, status=0/SUCCESS)
 Main PID: 1349 (rc_domino_scrip)
    Tasks: 157 (limit: 8000)
   CGroup: /system.slice/domino.service
           ├─1349 /bin/sh /opt/ibm/domino/rc_domino_script start
           ├─1445 /opt/ibm/domino/notes/latest/linux/server
           ├─1556 /opt/ibm/domino/notes/latest/linux/logasio NOTESLOGGER reserved
           ├─1641 /opt/ibm/domino/notes/latest/linux/event
           ├─2523 /opt/ibm/domino/notes/latest/linux/update
           ├─2524 /opt/ibm/domino/notes/latest/linux/replica
           ├─2525 /opt/ibm/domino/notes/latest/linux/router
           ├─2526 /opt/ibm/domino/notes/latest/linux/amgr -s
           ├─2527 /opt/ibm/domino/notes/latest/linux/adminp
           ├─2528 /opt/ibm/domino/notes/latest/linux/smtp
           ├─2529 /opt/ibm/domino/notes/latest/linux/daosmgr
           ├─2532 /opt/ibm/domino/notes/latest/linux/dbmt -compactThreads 1 -updallThreads 1 -range 4:00AM 6:00AM -compactNdays 5 -force 1 -nounread
           ├─2598 /opt/ibm/domino/notes/latest/linux/amgr -e 1
           ├─2925 /opt/ibm/domino/notes/latest/linux/cldbdir
           ├─3072 /opt/ibm/domino/notes/latest/linux/clrepl
           ├─3265 /opt/ibm/domino/notes/latest/linux/autorepair
           └─4268 /opt/ibm/domino/notes/latest/linux/amgr -e 2

Nov 27 15:48:54 systemd[1]: Starting IBM Domino Server (notes)...
Nov 27 15:48:55 rc_domino_script[1120]: Archived log file to '/local/notesdata/notes_181127_154855.log'
Nov 27 15:48:55 rc_domino_script[1120]: Starting Domino for xLinux (notes)
Nov 27 15:48:55 rc_domino_script[1120]: Warning: Cannot write to [FTBasePath] '/local/ft' (/local/ft)
Nov 27 15:48:55 rc_domino_script[1120]: done PID is 1349
Nov 27 15:48:55 systemd[1]: Started IBM Domino Server (notes).
[root@serv02 ~]# 

If everything is set correct, you will now have a Domino V10 server running on RHEL 7.4