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 https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml
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.

[InstallationError]
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.
[root@esxi:~]

Digging into the logs, I found the following clues

vmkernel.log:
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.log:
esxupdate: 2099635: root: ERROR: File "/build/mts/release/bora-10302608/bora/build/esx/release/vmvisor/sys-boot/lib64/python3.5/shutil.py", 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=https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml
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.0.29.9.3-7vmw.670.0.0.8169922, VMW_bootbank_net-forcedeth_0.61-2vmw.670.0.0.8169922, VMW_bootbank_net-igb_5.0.5.1.1-5vmw.670.0.0.8169922, VMW_bootbank_net-ixgbe_3.7.13.7.14iov-20vmw.670.0.0.8169922, VMW_bootbank_net-libfcoe-92_1.0.24.9.4-8vmw.670.0.0.8169922, 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.0.2.0.4-1vmw.670.0.0.8169922, 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.0.25.0.2-14vmw.670.0.0.8169922, VMW_bootbank_qfle3i_1.0.2.3.9-3vmw.670.0.0.8169922, 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.0.40.9.3-5vmw.670.0.0.8169922, 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
[root@esxi:~]


[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 
	Dim el As NOTESJSONELEMENT
	
	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: https://tools.ietf.org/html/rfc6901


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.

%REM
	Sub testJsonNavGetNthElement
%END REM
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)
	Stop
	Set el = jsnav.GetNthElement(1)
	Stop
	Set el = jsnav.GetNthElement(2)
	Stop
	Set el = jsnav.GetNthElement(3)
	Stop
	Set el = jsnav.GetNthElement(1000)
	Stop
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

Dim el As NOTESJSONELEMENT
Dim arr As NOTESJSONARRAY
	
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()

Dim obj As NOTESJSONOBJECT
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.

%REM
	Library 10010.http
	Created Jan 1, 2019 by Ulrich Krause/singultus
	Description: Comments for Library
%END REM
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 = "firstname.lastname@tld.de"
	password = "pAssw0rd"
	
	URL = "https://yourserver/names.nsf/($certifiers)?readviewentries&amp;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))

	Dim el As NOTESJSONELEMENT
	Dim arr As NOTESJSONARRAY
	Dim obj As NOTESJSONOBJECT

	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

https://marmelab.com/blog/2018/04/03/how-to-track-and-fix-memory-leak-with-nodejs.html

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

node –inspect=192.168.178.133:9229 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=192.168.178.133:9229″ 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.

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

%REM
	Class DqlWrapper
%END REM
Public Class DqlWrapper
	
	m_query As String
	m_session As NotesSession
	m_db As NotesDatabase
	m_ndq As NotesDominoQuery
	
	%REM
		Sub New
	%END REM
	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()
		Else
			' // do some error handling
		End if
	End Sub

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

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

	%REM
		Property query
	%END REM	
	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

%REM
	Agent dql.execute
	Created Dec 30, 2018 by Ulrich Krause/singultus
%END REM
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
	Else 
		
		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 )

[Unit]
Description=domino-node-list sample

[Service]
ExecStart=/git/domino-node-list/app.js
Restart=always
User=root
Group=root
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/git/domino-node-list/

[Install]
WantedBy=multi-user.target

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/multi-user.target.wants/domino-db.service 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 serv02.fritz.box systemd[1]: Started domino-node-list sample.
Dec 30 10:24:04 serv02.fritz.box systemd[1]: Starting domino-node-list sample...
Dec 30 10:24:05 serv02.fritz.box 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 https://www.eknori.de/2018-12-28/problem-using-proton-with-proton_ssl1-enabled/

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 make_keyring.sh 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": "serv02.fritz.box",
    "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: {
                    rootCertificate,
                    clientCertificate,
                    clientKey
            }    
    };

    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("serv02.fritz.box", "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_ADDRESS=serv02.fritz.box
[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 https://gitlab.com/obusse/domino-node-list
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\" &amp;&amp; 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 {
useServer
} = require('@domino/domino-db');

// proton config
const serverConfig = {
"hostName": "127.0.0.1",
"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

1. PROTON_SSL=0

First test with

PROTON_AUTHENTICATION=anonymous
PROTON_LISTEN_ADDRESS=0.0.0.0
PROTON_LISTEN_PORT=3002

12/28/2018 04:36:56.50 AM PROTON> Listening on 0.0.0.0:3002, 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 http://eknori.blogsite.org:3000/

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

PROTON_SSL=1

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*
PROTON_AUTHENTICATION=anonymous
PROTON_KEYFILE=eknori.kyr
PROTON_LISTEN_ADDRESS=0.0.0.0
PROTON_LISTEN_PORT=3002
PROTON_SSL=1
PROTON_TRACE_REQUEST=1
PROTON_TRACE_SESSION=1

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 0.0.0.0:3002, 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
PROTON Listening on 0.0.0.0:3002, SSL-ENABLED
HTTP Server Listen for connect requests on TCP Port:80, 443

When I know try to open https://eknori.blogsite.org:3000, I get

Secure Connection Failed
The connection to eknori.blogsite.org:3000 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
DNT 1
Host eknori.blogsite.org:3000
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 eknori.blogsite.org:3002
CONNECTED(00000003)
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 = eknori.blogsite.org
verify return:1
Certificate chain
0 s:/CN=eknori.blogsite.org
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
-----BEGIN CERTIFICATE-----
MIIFkzCCBHugAwIBAgISA02ojw4Nx7YNceZNEQjzQ6HRMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODExMjQwOTI5NDdaFw0x
OTAyMjIwOTI5NDdaMB4xHDAaBgNVBAMTE2Vrbm9yaS5ibG9nc2l0ZS5vcmcwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCF64IGqJ1K6AB4Is7KasQEdaiv
gN5Fh1VUulz8xY/hCshgyIP46weexrU6jpmUE/DUQSuC/YfzpF7wohZrtd+4AHwd
Hu+ICmGh8tGa1KRv2j/Gpu2Cj1h1uXms+qdMBX/fQzQ9pM/8o4PS1NvUkt58dpL0
1zpbyr9ibbfGV1VPE68zCDWYT+S1VhC/sXt09B32zsd+LC5bJgtqbYUa5dghbDnY
G7qLI3ahy/atRMD4+Qdyk4yJqqQO58LMKO/+h9er8JNxMXoDDaT54Sq7owRHm3Vn
EkomPPriIXEizqXd1GwpAYc/uiaOYUorUFnoA40u8Gi2RlTHh2ze6rLzplrnAgMB
AAGjggKdMIICmTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG
CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFGUvZj1OYKlDBGAiMref
eMYoSN0dMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUF
BwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNy
eXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNy
eXB0Lm9yZy8wUgYDVR0RBEswSYITZWtub3JpLmJsb2dzaXRlLm9yZ4IPZWtub3Jp
LnNwZG5zLmRlgg9la25vcmkuc3BkbnMuZXWCEGVrbm9yaS5zcGRucy5vcmcwTAYD
VR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYa
aHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEFBgorBgEEAdZ5AgQCBIH2BIHz
APEAdwB0ftqDMa0zEJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAWdFRKvdAAAE
AwBIMEYCIQCbF0dO0sH/+n0JgzUdKrNQdSXi0icdC/tcgoZJMWbBsQIhALrmWoIq
aOAryVyB5BdiIaCJ4gkQpltzZlMXsf2a8S6EAHYAY/Lbzeg7zCzPC3KEJ1drM6SN
YXePvXWmOLHHaFRL2I0AAAFnRUSr+gAABAMARzBFAiARZMQ/xNmVsD1AtFKhsjP/
JT7Ly1w/4t1lpCc9llWC6gIhAL9AvzdQfP3JSezUftlhbQwUq8pl87yilvM7tF/6
fJm9MA0GCSqGSIb3DQEBCwUAA4IBAQBWBIEnaXToqKlLepvxllJzJRaG4IdtDpHC
BN+bSsf5pCR9a1IOiJURdneLUF2RFYEtK8YOsWhEgpFodC/lEGC7P9AlPwK24/vO
Wyd2jPbCgpyzmp+8Hc6QLhttxz+Pd+MrjKFuYhpFkGqigAZGywiz12wPjhNOwhkI
iLpGENPnfJA/DPD01Zk0/4+toAhayuKqzVVB47W2O88dHB5TpsknGwVAWBAouTpm
hy8mHuVNk0+fwXGn739kLgZlsv95NWLCLIo5InJ5eHSQKgAU9uzRo5fzFXdRgxHk
w+4uKbruLxzi2IXUA214xEycylq49/jXePphfAsPXDvMYtmQRLcx
-----END CERTIFICATE-----
subject=/CN=eknori.blogsite.org
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
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: 49DA29C84DEFE38DAE3B7458F1E59859145DC5C27A27B248E1AC1958175F3BCC
Session-ID-ctx:
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)
read:errno=0

I also tried a simple https://eknori.blogsite.org:3000. 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 https://raw.githubusercontent.com/creationix/nvm/master/install.sh | 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 

Use

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: subscription.rhsm.redhat.com:443/subscription
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
./install

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

IBM

https://www.ibm.com


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 serv02.fritz.box systemd[1]: Starting IBM Domino Server (notes)...
Nov 27 15:48:55 serv02.fritz.box rc_domino_script[1120]: Archived log file to '/local/notesdata/notes_181127_154855.log'
Nov 27 15:48:55 serv02.fritz.box rc_domino_script[1120]: Starting Domino for xLinux (notes)
Nov 27 15:48:55 serv02.fritz.box rc_domino_script[1120]: Warning: Cannot write to [FTBasePath] '/local/ft' (/local/ft)
Nov 27 15:48:55 serv02.fritz.box rc_domino_script[1120]: done PID is 1349
Nov 27 15:48:55 serv02.fritz.box 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


Support

Ist schon lustig, wenn man mit dem Support in einer remote session sitzt, und der Supporter ganz genau wissen will, welche Client Version man verwendet, obwohl es sich um ein Server Problem handelt.

Aber auch die Server Version ist interessant. Und selbstverständlich bekommt man nur Support, wenn man alles auf supporteten Betriebssystemen installiert hat.

Der Supporter braucht das nicht.

Er verwendet Windows 10 als Betriebssystem für den Domino 10. Und fragt per Ticketsystem zurück, warum er die übermittelten .nsf in seinem Client nicht öffnen kann.  Nun ja, ODS53 geht nun mal nur mit V10 …


[V10] – nserver -c command not working properly when used with te amgr run

Andy Brunner informed me a couple of days ago that using the “te amgr run” in a server program document command does no longer work after upgrading the server to Domino V10.

You will get an error message on the console similar to this one

Tell AMGr Run ‘LE4D.nsf’ ‘letsencrypt’
15.10.2018 07:24:11 AMgr: Start executing agent ‘ ‘ in ‘LE4D.nsf’
15.10.2018 07:24:11 AMgr: Error attempting to load agent ‘ ‘ in ‘LE4D.nsf’: Entry not found in index

I have created a case with IBM support. The case number is TS001489373 and the issue is tracked unter SPR# GFALB5NKTV.

The case has already been escalated to development and they are investigating the issue and I’m awaiting their feedback.

In the meantime, you can use the following workaround ( Thanks to Andy Brunner )

Create a text file on the server. The file can contain one or more lines where the syntax is the same as when you start an agent from the Domino console.

Tell AMgr Run “LE4D.nsf” ‘letsencrypt’

Next change your program document to use the text file as input


Accessing the IDVAULT with LotusScript

NotesIdVault and NotesUserId are new classes as of IBM Notes 9.0.1 FP9. These classes provide a representation of the secure storage facility for UserIDs that may be configured for Domino by policy.

The classes are also available in Java and JavaScript.

This sound promising. As a developer you are now able to access the IDVAULT programmatically, get and put UserIds, resetPassword, mark an ID as active or inactive and force a resync if an ID becomes corrupt for some reason.

Well, this is what I thought when I first read about the new classes.

Let us take a closer look at some methods.

When you look at the list of methods in the NotesIDVault class, you can easily see that a couple of methods are not available.

  • ExtractIdFile
  • MarkIdActive
  • MarkIdInactive

I have tested other methods, and all of them do what they are supposed to do. Except GetUserId.

GetUserId returns an NotesUserID. This might not be too bad, because this is exactly what we want to get when calling this method. But, if you expect to be able to download the id file to the local file system, you’ll be disappointed.

The documentation says

This object is primarily used to obtain the names of the private encryption keys which are available for use within the UserID object.

Frankly said, completely useless.

You could get the attached user.id with traditional Lotsuscript ( locate the document, detach the embedded object ), but I would expect this as a method in either the NotesIDVault or NotesUserId class.  Once again, half backed stuff. I hope that the guys and gals at HCL are more open for enhancement requests than IBM was.

UPDATE: There IS actually a method in the NotesIDVault class to detach the user.id from the vault document to the local file system!

At the bottom of this article you find an updated version of my IDVAULT class the extends the NotesIdVault class and lets you extract an id file.

A couple of years ago, I wrote my own IDVault class in Lotusscript. It uses LS2CAPI functions that let you do more than the provided LS classes.

The class provides methods to

  • IdFileGet()
  • IdFilePut()
  • IdFileExtract()
  • ResetPassword()
  • MarkActive()
  • MarkInactive()

IdFileGet() lets you download the user.id file from the vault document to the local file system. Much better than the method from the LS class, isn’t it.

IdFileExtract() lets you get the user.id from the vault document to the local file system WITHOUT knowing the password. It uses the ResetPassword method to apply a new password to the user.id and some “magic” to llet the original vault document remain unchanged.

The magic behind this is that you must make a copy of the vault document prior to resetting the password. The copy MUST be stored in another  database. After you have downloaded the ID with IDFileGet(), you must replace the vault document with the copy.

Here is a sample agent ( the agent signer must be a password reset authority in the IDVault )

%REM
	Agent test
	Created Sep 18, 2015 by Ulrich Krause/singultus
%END REM

Option Declare
Use "IDVault"

Sub Initialize
	Dim idv_db As NotesIdVaultDb
	Dim bck_db As NotesIdVaultDb
	Dim vaultServer As String
	Dim vaultDb As String
	Dim vaultDb_backup As String
	
	vaultServer = "develop1/gotham"
	vaultDb = "IBM_ID_VAULT\batcave.nsf"
	vaultDb_backup =  "IBM_ID_VAULT\batcave_bck.nsf"
	
	Dim idv_doc As NotesIdVaultDocument

	Set idv_db = New NotesIdVaultDb(vaultServer, vaultDb)
	
	If ( idv_db.IsConnected() ) then
		Set idv_doc = New NotesIdVaultDocument(idv_db.db,nothing)
		Set bck_db = New NotesIdVaultDb(vaultServer, vaultDb_backup)
		
		If ( bck_db.IsConnected() ) Then
			Set idv_doc = New NotesIdVaultDocument(idv_db.db, bck_db.db)
		Else
			Set idv_doc = New NotesIdVaultDocument(idv_db.db, Nothing)
		End If
		
		idv_doc.ServerName = vaultServer
		idv_doc.UserName = "cn=Harvey Dent/o=gotham"
		idv_doc.Password = "hdent001"
		idv_doc.IdFilePathName = "C:\Notes\Data\ids\people\backup\hdent_with_new_pwd.id"
		'Call idv_doc.ResetPassword()
		'Call idv_doc.IdFileGet()
		'Call idv_doc.IdFilePut()
		Call idv_doc.IdFileExtract()
		'Call idv_doc.markInactive()
		'Call idv_doc.markActive()
		MsgBox idv_doc.LastErrorString

	End if
	
End Sub

Here is the updated version. This version is also platform independend