Repository: exasol/bucketfs-client
Pull request #53: #38: Updated documentation and Project Keeper.
Author: login: redcatbear
Labels: name: documentation
description: User guides, tutorials, specifications
Created: 2025-05-06T13:52:43Z | Merged: 2025-05-14T06:28:44Z

Closes #38.

Linked issue #38: Improvement ideas for the User Guide
1. Markup for the hyperlink near "SuSE: OpenJDK build of the Leap project" is seemingly broken.
2. The tool seems to have quite a wide set of requirements. In my Ubuntu 22.04 running in a VirtualBox VM (host system is Windows 11) it's failing with
```
alex@alex-VirtualBox:~$ /home/alex/Downloads/bfsc-linux_x86 ls bfss://localhost:2581/default
The current machine does not support all of the following CPU features that are required by the image: [CX8, CMOV, FXSR, MMX, SSE, SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, POPCNT, LZCNT, AVX, AVX2, BMI1, BMI2, FMA].
Please rebuild the executable with an appropriate setting of the -march option.alex@alex-VirtualBox:~$
```
3. A trailing newline after the error message above would be useful.
4. It is not crystal clear from the User Guide if the native executable supports "Linux" or "Ubuntu Linux".
5. Again to wide system requirements, on Rocky Linux release 8.9 (Green Obsidian) the tool fails with
```
$ ~/Downloads/bfsc-linux_x86 bfsc ls bfss://localhost:2581/default
<path>/bfsc-linux_x86: /lib64/libc.so.6: version `GLIBC_2.32' not found (required by <path>/bfsc-linux_x86)
<path>/bfsc-linux_x86: /lib64/libc.so.6: version `GLIBC_2.34' not found (required by <path>/bfsc-linux_x86)
```
6.
> For release 1.1.2 this is Ubuntu 22.04 with glibc 2.32.

Maybe one can refresh this part (the latest version is 2.0.0)?

7. "Retrieving the Password" section.
`/exa/etc/EXAConf` is not a publicly available interface. Luckily, one can use ConfD job [bucketfs_info](https://docs.exasol.com/db/latest/confd/jobs/bucketfs_info.htm).
Linked issue comments: user: login: ckunki
created_at: 2024-06-03T08:42:54Z
body: Notes for potential updates to the documentation: 

The version of glibc installed on a linux system can be inquired with 
```shell 
$ ldd --version 
```

Which will print `ldd (GNU libc) 2.38` for example.

https://askubuntu.com/questions/1345342 describes how to install a different version of glibc 
* for a single user 
* without requiring root permissions (TBC).

Note: The error message described above is displayed by the operating system or the glue code generated by the binary generator, see [raalvm/setup-graalvm](https://github.com/graalvm/setup-graalvm). Exasol bucketfs-client can probably not change the formatting of the error message, e.g. insert a newline.

See [graalvm, native-image, BuildOptions](https://www.graalvm.org/jdk20/reference-manual/native-image/overview/BuildOptions/) for build options to Graal VM, that could be added to the command `native-image` in GitHub workflow [native-image.yml](https://github.com/exasol/bucketfs-client/blob/main/.github/workflows/build-native-images.yml#L43C1-L43C18).

> -march: generate instructions for a specific machine type. Defaults to x86-64-v3 on AMD64 and armv8-a on AArch64. Use -march=compatibility for best compatibility, or -march=native for best performance if a native executable is deployed on the same machine or on a machine with the same CPU features. To list all available machine types, use -march=list.
author_association: COLLABORATOR
user: login: redcatbear
created_at: 2024-06-03T13:07:05Z
body: @ckunki , please fill custom fields and add to next Kehrwoche.
author_association: CONTRIBUTOR

Diff:
diff --git a/.github/workflows/broken_links_checker.yml b/.github/workflows/broken_links_checker.yml
index 90488ca..09e4bac 100644
--- a/.github/workflows/broken_links_checker.yml
+++ b/.github/workflows/broken_links_checker.yml
@@ -1,35 +1,44 @@
-# Generated by Project Keeper
-# https://github.com/exasol/project-keeper/blob/main/project-keeper/src/main/resources/templates/.github/workflows/broken_links_checker.yml
+# This file was generated by Project Keeper.
 name: Broken Links Checker
-
 on:
   schedule:
-    - cron: "0 5 * * 0"
-
+    - {
+      cron: 0 5 * * 0
+    }
+  workflow_dispatch: null
 jobs:
   linkChecker:
     runs-on: ubuntu-latest
-    permissions:
+    permissions: {
       contents: read
+    }
     defaults:
-      run:
-        shell: "bash"
-    concurrency:
-      group: ${{ github.workflow }}-${{ github.ref }}
+      run: {
+        shell: bash
+      }
+    concurrency: {
+      group: '${{ github.workflow }}-${{ github.ref }}',
       cancel-in-progress: true
+    }
     steps:
-      - uses: actions/checkout@v4
-      - name: Configure broken links checker
+      - {
+        id: checkout,
+        uses: actions/checkout@v4
+      }
+      - id: configure-broken-links-checker
+        name: Configure broken links checker
         run: |
           mkdir -p ./target
           echo '{"aliveStatusCodes": [429, 200], "ignorePatterns": [' \
-               '{"pattern": "^https?://(www|dev).mysql.com/"},' \
-               '{"pattern": "^https?://(www.)?opensource.org"}' \
-               '{"pattern": "^https?://(www.)?eclipse.org"}' \
-               '{"pattern": "^https?://projects.eclipse.org"}' \
-               ']}' > ./target/broken_links_checker.json
-      - uses: gaurav-nelson/github-action-markdown-link-check@v1
-        with:
-          use-quiet-mode: "yes"
-          use-verbose-mode: "yes"
+          '{"pattern": "^https?://(www|dev).mysql.com/"},' \
+          '{"pattern": "^https?://(www.)?opensource.org"}' \
+          '{"pattern": "^https?://(www.)?eclipse.org"}' \
+          '{"pattern": "^https?://projects.eclipse.org"}' \
+          ']}' > ./target/broken_links_checker.json
+      - id: run-broken-links-checker
+        uses: gaurav-nelson/github-action-markdown-link-check@v1
+        with: {
+          use-quiet-mode: yes,
+          use-verbose-mode: yes,
           config-file: ./target/broken_links_checker.json
+        }
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index 13df039..f813a11 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -16,7 +16,7 @@ on:
     
 jobs:
   matrix-build:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-24.04
     defaults:
       run: {
         shell: bash
diff --git a/.github/workflows/dependencies_check.yml b/.github/workflows/dependencies_check.yml
index f721b3d..2b1a598 100644
--- a/.github/workflows/dependencies_check.yml
+++ b/.github/workflows/dependencies_check.yml
@@ -45,9 +45,9 @@ jobs:
               org.sonatype.ossindex.maven:ossindex-maven-plugin:audit-aggregate \
               -Dossindex.reportFile=$(pwd)/ossindex-report.json \
               -Dossindex.fail=false
-      - name: Report Security Issues
-        id: security-issues
-        uses: exasol/python-toolbox/.github/actions/security-issues@main
+      - name: Create GitHub Issues
+        id: create-security-issues
+        uses: exasol/python-toolbox/.github/actions/security-issues@1.1.0
         with: {
           format: maven,
           command: cat ossindex-report.json,
diff --git a/.project-keeper.yml b/.project-keeper.yml
index 0e1e85c..6687aaa 100644
--- a/.project-keeper.yml
+++ b/.project-keeper.yml
@@ -14,7 +14,7 @@ excludes:
   - regex: "E-PK-CORE-26: '.github[/\\\\]workflows[/\\\\]release_droid_print_quick_checksum.yml' exists but must not exist. Reason: Release-droid workflows are replaced by release.yml"
   - regex: "E-PK-CORE-26: '.github[/\\\\]workflows[/\\\\]release_droid_prepare_original_checksum.yml' exists but must not exist. Reason: Release-droid workflows are replaced by release.yml"
 build:
-  runnerOs: ubuntu-latest
+  runnerOs: ubuntu-24.04
   freeDiskSpace: false
   exasolDbVersions:
     - "8.34.0"
diff --git a/dependencies.md b/dependencies.md
index a2e3c03..8751243 100644
--- a/dependencies.md
+++ b/dependencies.md
@@ -86,7 +86,7 @@
 [27]: https://maven.apache.org/plugins/maven-install-plugin/
 [28]: https://maven.apache.org/plugins/maven-resources-plugin/
 [29]: https://maven.apache.org/plugins/maven-site-plugin/
-[30]: http://docs.sonarqube.org/display/PLUG/Plugin+Library/sonar-maven-plugin
+[30]: http://docs.sonarqube.org/display/PLUG/Plugin+Library/sonar-scanner-maven/sonar-maven-plugin
 [31]: http://www.gnu.org/licenses/lgpl.txt
 [32]: https://maven.apache.org/plugins/maven-toolchains-plugin/
 [33]: https://github.com/itsallcode/openfasttrace-maven-plugin
diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md
index 2b484d9..2c13851 100644
--- a/doc/changes/changelog.md
+++ b/doc/changes/changelog.md
@@ -1,5 +1,6 @@
 # Changes
 
+* [2.2.1](changes_2.2.1.md)
 * [2.2.0](changes_2.2.0.md)
 * [2.1.2](changes_2.1.2.md)
 * [2.1.1](changes_2.1.1.md)
diff --git a/doc/changes/changes_2.2.1.md b/doc/changes/changes_2.2.1.md
new file mode 100644
index 0000000..6e3ff23
--- /dev/null
+++ b/doc/changes/changes_2.2.1.md
@@ -0,0 +1,27 @@
+# BucketFS Client 2.2.1, released 2025-05-13
+
+Code name: Documentation improvements on top of 2.2.0
+
+## Summary
+
+We improved the content, language and understandability of the BucketFS client user guide.
+
+Please note that we are in the process of removing some URL auto-magic that uses profile information, since that turned out to be confusing rather than convenient.
+
+We recommend using full BFS URLs, since the shortening will be removed with [#54](https://github.com/exasol/bucketfs-client/issues/54).
+
+The profile configuration as such will remain available, since it is required to hide passwords and point to server certificates.
+
+## Features
+
+* 38: Improved user documentation
+
+## Dependency Updates
+
+### Plugin Dependency Updates
+
+* Updated `com.exasol:project-keeper-maven-plugin:5.0.1` to `5.1.0`
+* Updated `org.apache.maven.plugins:maven-failsafe-plugin:3.5.2` to `3.5.3`
+* Updated `org.apache.maven.plugins:maven-surefire-plugin:3.5.2` to `3.5.3`
+* Updated `org.jacoco:jacoco-maven-plugin:0.8.12` to `0.8.13`
+* Updated `org.sonarsource.scanner.maven:sonar-maven-plugin:5.0.0.4389` to `5.1.0.4751`
diff --git a/doc/user_guide/user_guide.md b/doc/user_guide/user_guide.md
index f69fee7..228e5fb 100644
--- a/doc/user_guide/user_guide.md
+++ b/doc/user_guide/user_guide.md
@@ -4,7 +4,7 @@ The BucketFS Client (BFSC) is a client program for Exasol's BucketFS distributed
 
 BFSC allows you to read and write the contents of buckets.
 
-A bucket is a storage space on an Exasol cluster that automatically takes care of distributing files loaded onto it. Those files can then be used in [User Defined Functions (UDFs)](https://docs.exasol.com/database_concepts/udf_scripts.htm), e.g. for [Virtual Schemas](https://docs.exasol.com/db/latest/database_concepts/virtual_schemas.htm) which are a special type of UDFs. See [official documentation](https://docs.exasol.com/db/latest/database_concepts/bucketfs/bucketfs.htm) for more details on BucketFS.
+A bucket is a storage space on an Exasol cluster that automatically takes care of distributing files loaded onto it. Those files can then be used in [User Defined Functions (UDFs)](https://docs.exasol.com/database_concepts/udf_scripts.htm). An example would be [Virtual Schemas](https://docs.exasol.com/db/latest/database_concepts/virtual_schemas.htm) which are a special type of UDFs. See [official documentation](https://docs.exasol.com/db/latest/database_concepts/bucketfs/bucketfs.htm) for more details on BucketFS.
 
 ## Use Cases
 
@@ -19,18 +19,25 @@ The most common use case for BucketFS is storing files on it that UDFs need:
 
 The client is implemented in Java and provides native executables for x86 CPU with operating system Ubuntu Linux or Windows.
 
-Platforms
+#### Supported Platforms
+
 | Platform | Available?   | Notes |
 |----------|--------------|-------|
-| Linux    | &#x2705; yes | Built using GitHub hosted runner [latest_ubuntu](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources). For release 1.1.2 this is Ubuntu 22.04 with glibc 2.32. |
-| Windows  | &#x2705; yes | When downloading the native executable some browsers may display a warning about security risks imposed by downloading unsigned binaries. |
-| macOS    | &#x274c; no  | Latest macOS Ventura (e.g. version 13.2.1) refuses to execute unsigned binaries. |
+| Linux    | &#x2705; yes | ¹⁾    | 
+| Windows  | &#x2705; yes | ²⁾    | 
+| macOS    | &#x274c; no  | ³⁾    |
+
+¹⁾ Release 2.2.1 is tested on Ubuntu 24.04.
+
+²⁾ When downloading the native executable, some browsers may display a warning about security risks imposed by downloading unsigned binaries.
+
+³⁾ Latest macOS Ventura (e.g. version 13.2.1) refuses to execute unsigned binaries.
 
 Signed binaries for Windows and macOS will be provided as soon as a signing process is established.
 
-For all platforms not supported currently or in case you do not want to execute an unsigned binary on your platform please refer to the [jar file](#running-the-jar-file) provided with each release.
+For all platforms not supported currently or in case you do not want to execute an unsigned binary on your platform, please refer to the [jar file](#running-the-jar-file) provided with each release.
 
-To make BFSC really handy you can either rename the binary or set an alias
+To make using the BFSC more convenient, you can either rename the binary or set an alias
 
 ```shell
 alias bfsc='bfsc-linux_x86'
@@ -38,7 +45,7 @@ alias bfsc='bfsc-linux_x86'
 
 ### Running the JAR File
 
-Besides executing the binaries you can also download BFSC's JAR file and let a Java VM execute it.
+Besides executing the binaries, you can also download BFSC's JAR file and let a Java VM execute it.
 
 For that you need Java 17 or later, the installation procedure depends on your operating system.
 
@@ -54,7 +61,7 @@ For that you need Java 17 or later, the installation procedure depends on your o
       ```
     * SuSE: [OpenJDK build of the Leap project](https://software.opensuse.org/download/package?package=java-17-openjdk&project=openSUSE%3AFactory)
 
-As soon as Java 17 is available you can run BFSC's JAR file the following command:
+When Java 17 is available, you can run BFSC's JAR file with the following command:
 
 ```shell
 java -jar "<path-to-bfsc-jar>" <command> <option> ...
@@ -74,15 +81,15 @@ Sub-commands control the action that BFSC is taking. For each type of action the
 bfsc <sub-command> <option> ...
 ```
 
-BFSC supports the following sub commands to inspect and manipulate files in the BucketFS. You can use a [configuration file](#configuration-file) or rely on default values to abbreviate URLs in BucketFS.
+BFSC supports the following sub commands to inspect and manipulate files in the BucketFS.
 
-| Full                                           | Abbreviated           | Description                                        |
-|------------------------------------------------|-----------------------|----------------------------------------------------|
-| `bfsc ls bfs://localhost:2580/default`         |`bfsc ls`              | List the contents of BucketFS                      |
-| `bfsc ls bfs://localhost:2580/default/folder`  |`bfsc ls folder`       | List the contents of a directory in BucketFS       |
-| `bfsc cp a.jar bfs://localhost:2580/default/`  |`bfsc cp a.jar bfs:/`  | Upload a file from local fie system to BucketFS    |
-| `bfsc cp bfs://localhost:2580/default/a.jar .` |`bfsc cp bfs:/a.jar .` | Download a file from BucketFS to local file system |
-| `bfsc rm bfs://localhost:2580/a.jar`           |`bfsc rm /a.jar`       | Remove a file from BucketFS                        |
+| Command                                        | Description                                        |
+|------------------------------------------------|----------------------------------------------------|
+| `bfsc ls bfs://localhost:2580/default/`        | List the contents of BucketFS                      |
+| `bfsc ls bfs://localhost:2580/default/folder/` | List the contents of a directory in BucketFS       |
+| `bfsc cp a.jar bfs://localhost:2580/default/`  | Upload a file from local fie system to BucketFS    |
+| `bfsc cp bfs://localhost:2580/default/a.jar .` | Download a file from BucketFS to local file system |
+| `bfsc rm bfs://localhost:2580/a.jar`           | Remove a file from BucketFS                        |
 
 The following sections will first explain some general concepts and then describe each of the commands in detail.
 
@@ -93,12 +100,12 @@ The following sections will first explain some general concepts and then describ
 BFSC uses specific URLs to refer to files or paths in a bucket.
 
 ```
-bfs[s]://<host>:<port>/<path-in-bucket>
+bfs[s]://<host>:<port>/[<path-in-bucket>]
 ```
 
 The protocol is either `bfs` or `bfss` for a connection that is TLS secured.
 
-You specify the host or IP address of the machine where the BucketFS service runs. Each service has its own port that you set in the BucketFS service configuration so a port number identifies the service.
+You specify the host or IP address of the machine where the BucketFS service runs. Each service has its own port that you set in the BucketFS service configuration, so a port number identifies the service.
 
 The first path element is the name of the bucket. The next elements are the path relative to that bucket's root.
 
@@ -111,9 +118,9 @@ There are separate passwords for read- and write-operations:
 | Write operations | Upload files to BucketFS, delete files from the BucketFS   | Always password protected      |
 | Read operations  | List bucket contents, download files                       | Unprotected for public buckets |
 
-BFSC supports to read required passwords either from the profile in the [configuration file](#configuration-file) or from an interactive prompt hiding the characters typed by the user.
+BFSC supports reading required passwords either from the profile in the [configuration file](#configuration-file) or from an interactive prompt hiding the characters typed by the user.
 
-BFSC does not support to provide passwords on the command line to avoid the password showing up in the command history. As a general rule you should never put any credentials directly into a command line.
+BFSC does not support passwords on the command line to avoid the password showing up in the command history. As a general rule, you should never put any credentials directly into a command line.
 
 BFSC will normally not ask for a read-password interactively.
 * This is fine for public buckets as these are not password-protected.
@@ -122,7 +129,25 @@ BFSC will normally not ask for a read-password interactively.
 
 ### Retrieving the Password
 
-The passwords are usually stored in file `/exa/etc/EXAConf`:
+#### Variant 1: Reading the Password Using `confd_client` (Recommended)
+
+You can get the configuration of a BucketFS service with the `confd_client` tool (see also ["bucketfs_info"](https://docs.exasol.com/db/latest/confd/jobs/bucketfs_info.htm)):
+
+Here is an example of getting the decoded read-password. Replace the BucketFS service name (here `bfsdefault`) if necessary.
+
+```shell
+confd_client bucketfs_info bucketfs_name: bfsdefault | grep -oP 'read_passwd: \K.*' | base64 -d
+```
+
+And for the write-password:
+
+```shell
+confd_client bucketfs_info bucketfs_name: bfsdefault | grep -oP 'write_passwd: \K.*' | base64 -d
+```
+
+#### Variant 2: Reading the Password Directly From the EXAConf
+
+Please note that this variant requires filesystem access to the Exasol database. The passwords are usually stored in file `/exa/etc/EXAConf`:
 ```
 WritePasswd = <value>
 ReadPasswd = <value>
@@ -138,57 +163,36 @@ If the BucketFS server uses a self-signed certificate or the certificate is not
 
 ### Configuration File
 
-Besides specifying the complete URL on the command line you can use defaults for some parts of the URL.
-
-BFSC uses the following precedence for URI parts
-1. Supplied on the command line, or entered interactively in case of a [password](#password-protected-bucket-access)
-2. Data in your profile
-3. Hard coded default value for host and port
-
 You can define the profile in BFSC's configuration file in your home directory: `~/.bucketfs-client/config.ini`. On Windows the home directory is `%USERPROFILE%`.
 
-The configuration file uses the INI-file syntax. An INI file is divided into sections. Each section contains a number of lines defining a *profile*. The first line of each section specifies the name of the profile in brackets. The default profile's name is `default`. Each of the following lines of the section may assign a value to a variable.
+The configuration file uses the INI-file syntax. An INI file is divided into sections. Each section contains a number of lines defining a *profile*. The first line of each section specifies the name of the profile in brackets. The default profile's name is `default`. Each line after the section header assigns a value to a variable.
 
-BFCS uses the following elements of your profile with the specified hard coded default values:
+BFSC uses the following elements of your profile with the specified hard-coded default values:
 
 | Parameter                       | Variable         | Default value                   |
 |---------------------------------|------------------|---------------------------------|
-| Protocol of BucketFS server     | `protocol`       | `bfs` or `bfss`, default: `bfs` |
-| Host address of BucketFS server | `host`           | `localhost`                     |
-| Port                            | `port`           | `2580`                          |
 | Name of root bucket             | `bucket`         | (none)                          |
 | Password for read operations    | `password.read`  | (none)                          |
 | Password for write operations   | `password.write` | (none)                          |
 | Path to TLS certificate         | `certificate`    | (none)                          |
 
-Here is an example for the content of a configuration file for BFSC:
+Here is an example content of a configuration file for BFSC:
 ```
 [default]
-protocol=bfss
-host=1.2.3.4
-port=8888
 bucket=simba
 password.read=abc
 password.write=def
 certificate=/path/to/cert.crt
 ```
 
-Using this configuration file the command line
-```shell
-bfsc cp foo.jar bfs://1.2.3.4:8888/simba/drivers/foo.jar
-```
-... can then be abbreviated to:
-```shell
-bfsc cp foo.jar bfs:/drivers
-```
-
 ### Name of the Root Bucket
 
 BFSC cannot detect whether the URL on the command line contains a bucket name or directly starts with the path *inside* the bucket. If the profile in your configuration file specifies a bucket name, then BFSC will always prepend that bucket name to the path of the URL.
 
 If you want to provide the name of the root bucket on the command line then please do not set `bucket` in your profile.
 
-In case your profile specifes the name of the root bucket all of the following commands will list the contents of directory `folder` in the root bucket:
+In case your profile specifies the name of the root bucket all following commands will list the contents of directory `folder` in the root bucket:
+
 ```shell
 bfsc ls folder
 bfsc ls /folder
@@ -196,7 +200,7 @@ bfsc ls bfs:///folder
 bfsc ls bfs:/folder
 ```
 
-In case your profile does not specify the name of the root bucket all of the following commands will list the contents of root bucket `default`:
+In case your profile does not specify the name of the root bucket all following commands will list the contents of root bucket `default`:
 
 ```shell
 bfsc ls /default/
@@ -224,7 +228,7 @@ folder/
 
 BFSC appends a trailing slash `/` to separate directories from files potentially having the same name.
 
-If you want to list all *buckets* rather than the *contents* of a specific bucket, then please ensure to not not set `bucket` in your profile.
+If you want to list all *buckets* rather than the *contents* of a specific bucket, then please ensure to not set `bucket` in your profile.
 
 Example output for listing root buckets
 ```
@@ -232,7 +236,7 @@ default
 simba
 ```
 
-With option `-r` or `--recursive` you can obtain a recursive listing of all contents inside a directory of a bucket. However when listing *buckets* then BFSC will ignore option `-r`.
+With option `-r` or `--recursive` you can get a recursive listing of all contents inside a directory of a bucket. However, when listing *buckets* then BFSC will ignore option `-r`.
 
 Example (abbreviated):
 ```shell
@@ -250,7 +254,7 @@ With `bfsc cp` you can copy files between BucketFS and the local file system.
 bfsc cp <from> <to>
 ```
 
-In the majority of all cases you will copy files _to_ a bucket. For example if you want to install a library that you plan to use in a Python or Java UDF (User Defined Function):
+In the majority of all cases you will copy files _to_ a bucket. For example, if you want to install a library that you plan to use in a Python or Java UDF (User Defined Function):
 
 ```shell
 bfsc cp foo-driver-1.2.3.jar bfs://192.168.0.1:2580/default/drivers/foo-driver-1.2.3.jar
@@ -269,41 +273,41 @@ Command `cp` supports recursive copying:
 bfsc cp -r Dir bfs:/
 ```
 
-However there are some special cases explained in the following sections.
+However, there are some special cases explained in the following sections.
 
-#### Ambigue Entries in BucketFS
+#### Ambiguous Entries in BucketFS
 
-BucketFS differs from the local file system as it may contain *ambigue* entries. An ambigue entry represents a regular file and a directory at the same time. Obviously you cannot download both flavors of such an entry to the local file system and BFSC will abort with an error message.
+BucketFS differs from the local file system as it may contain *ambiguous* entries. An ambiguous entry represents a regular file and a directory at the same time. You cannot download both flavors of such an entry to the local file system, and BFSC will abort with an error message.
 
-You can however tell BFSC to download *one* of the flavors of such an ambigue entry.
-* For recursively downloading the directory please specify option `-r` *and* append a slash `/` to the name of the ambigue entry.
-* For downloading the regular file omit both of them.
+You can, however, tell BFSC to download *one* of the flavors of such an ambiguous entry.
+* For recursively downloading the directory please specify option `-r` *and* append a slash `/` to the name of the ambiguous entry.
+* For downloading the regular file, omit both of them.
 
-| Command (abbreviated)      | Semantics                                |
-|----------------------------|------------------------------------------|
-| `bfsc cp bfs:/ambigue/`    | error message                            |
-| `bfsc cp -r bfs:/ambigue`  | error message                            |
-| `bfsc cp bfs:/ambigue`     | download regular file `ambigue`          |
-| `bfsc cp -r bfs:/ambigue/` | recursively download directory `ambigue` |
+| Command (abbreviated)        | Semantics                                  |
+|------------------------------|--------------------------------------------|
+| `bfsc cp bfs:/ambiguous/`    | error message                              |
+| `bfsc cp -r bfs:/ambiguous`  | error message                              |
+| `bfsc cp bfs:/ambiguous`     | download regular file `ambiguous`          |
+| `bfsc cp -r bfs:/ambiguous/` | recursively download directory `ambiguous` |
 
-For non-ambigue entries the trailing slash is not relevant.
+For non-ambiguous entries the trailing slash is not relevant.
 
-For ambigue entries on lower levels during recursive download BFSC will report a warning and only download the directory flavor of the ambigue entry.
+For ambiguous entries on lower levels during recursive download, BFSC will report a warning and only download the directory flavor of the ambiguous entry.
 
 #### Existing Entries in the Local File System
 
-When downloading from BucketFS the target in the local file system might already exist and could be either a directory or a regular file.
+When downloading from BucketFS, the target in the local file system might already exist and could be either a directory or a regular file.
 
 The following table shows all cases assuming the local file system contains a regular file `b.txt` and a directory `B`.
 
-| Command (abbreviated)  | Semantics / error message                              |
-|------------------------|--------------------------------------------------------|
-| `cp bfs://a.txt b.txt` | download regular file `a.txt` and rename it to `b.txt` |
+| Command (abbreviated)  | Semantics / error message                                                                                             |
+|------------------------|-----------------------------------------------------------------------------------------------------------------------|
+| `cp bfs://a.txt b.txt` | download regular file `a.txt` and rename it to `b.txt`                                                                |
 | `cp bfs://a.txt B`     | download regular file `a.txt` into existing local directory `B` and keep the original filename resulting in `B/a.txt` |
-| `cp -r bfs://A B`      | recursively download directory `A` to `B/A`            |
-| `cp -r bfs://A b.txt`  | error message                                          |
+| `cp -r bfs://A B`      | recursively download directory `A` to `B/A`                                                                           |
+| `cp -r bfs://A b.txt`  | error message                                                                                                         |
 
-When downloading a directory from BucketFS you can rename the directory by specifying a local name that does not exist, yet.
+When downloading a directory from BucketFS, you can rename the directory by specifying a local name that does not exist, yet.
 
 ### Deleting Files From a Bucket
 
@@ -316,7 +320,65 @@ bfsc rm folder/b.txt
 bfsc rm -r folder
 ```
 
-As for the other commands command `rm` supports recursive removal, too. However there are some special remarks
+As for the other commands command `rm` supports recursive removal, too. However, there are some special remarks
 * BFSC will not display an error message if you specify `-r` for deleting a regular file.
-* With option `-r` BFSC will remove files, directories, and ambigue entries.
-* Applying `rm` without option `-r` on an ambigue entry will remove only the regular file but not the directory with the same name.
+* With option `-r` BFSC will remove files, directories, and ambiguous entries.
+* Applying `rm` without option `-r` on an ambiguous entry will remove only the regular file but not the directory with the same name.
+
+## Questions and Answers (Q&A)
+
+### What are the BucketFS Default Values?
+
+A standard Exasol installation has a default BucketFS service with a single bucket. Here are the parameters for this default setup.
+
+| Parameter      | Default Value |
+|----------------|---------------|
+| Service name   | `bfsdefault`  |
+| BFS HTTP port  | 2580          |
+| BFS HTTPS port | 2581          |
+| Bucket         | `default`     |
+
+### How can I Experiment Safely With BucketFS?
+
+If you want to familiarize yourself with BucketFS on your local machine, try Exasol's `docker-db`. Here is a command that starts a `docker-db` container and forwards the ports to your `localhost`.
+
+```shell
+docker run --name exasoldb \
+ -p 127.0.0.1:8563:8563  -p 127.0.0.1:2580:2580 -p 127.0.0.1:2581:2581 \
+ -p 127.0.0.1:443:443 -p 127.0.0.1:2222:22 \
+ --detach --privileged --stop-timeout 120  exasol/docker-db:latest
+```
+
+This forwards the database port (8563), BFS (2580) and BFSS (2581), HTTPS (443) and SSH (22 → 2222).
+
+If you want to extract the read / write passwords for the buckets, use `docker exec` to enter the container.
+
+You can use `openssl s_client` to take a look at the self-signed certificate that the `docker-db` presents:
+
+```shell
+openssl s_client -connect 127.0.0.1:2581 -showcerts </dev/null
+```
+
+Note that while this method is good enough for local experiments with `docker-db`, you should **never trust** a certificate presented by a server process without verification.
+
+```shell
+docker exec -it <docker-container-id> bash
+```
+
+Then follow the steps described in ["Password-Protected Bucket Access"](#password-protected-bucket-access).
+
+To verify that you have the right certificate stored (e.g., in `~/.bucketfs-client/server-cert.pub`) you can use `curl` and list the bucket contents:
+
+```shell
+curl --resolve exacluster.local:2581:127.0.0.1 --cacert ~/.bucketfs-client/server-cert.pub https://r:<read-password>@exacluster.local:2581
+```
+
+The self-signed certificate of the `docker-db` instance uses `exacluster.local` as hostname. Since the hostname we give `curl` must match the one in the certificate, we need to help with resolving that host to the local IP.
+
+If the line above outputs the bucket name `default`, you are all set up.
+
+### Where do I Find the TLS Certificate That the BucketFS Server uses?
+
+The certificate is by default located under `/exa/etc/ssl/ssl.crt`.
+
+Please note that everytime you start a fresh `docker-db`, you get a new self-signed certificate. Please take that into account when you experiment with BucketFS.
\ No newline at end of file
diff --git a/pk_generated_parent.pom b/pk_generated_parent.pom
index df8500a..c6201f6 100644
--- a/pk_generated_parent.pom
+++ b/pk_generated_parent.pom
@@ -3,7 +3,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.exasol</groupId>
     <artifactId>bucketfs-client-generated-parent</artifactId>
-    <version>2.2.0</version>
+    <version>2.2.1</version>
     <packaging>pom</packaging>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -60,7 +60,7 @@
             <plugin>
                 <groupId>org.sonarsource.scanner.maven</groupId>
                 <artifactId>sonar-maven-plugin</artifactId>
-                <version>5.0.0.4389</version>
+                <version>5.1.0.4751</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -160,7 +160,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>3.5.2</version>
+                <version>3.5.3</version>
                 <configuration>
                     <!-- Set the highest log level for coverage testing, so that we
           have a chance to reach branches in the logging lambdas too. -->
@@ -290,7 +290,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-failsafe-plugin</artifactId>
-                <version>3.5.2</version>
+                <version>3.5.3</version>
                 <configuration>
                     <!-- Set the highest log level for coverage testing, so that we have a chance to reach branches in the logging lambdas too. -->
                     <argLine>-Djava.util.logging.config.file=src/test/resources/logging.properties ${argLine}</argLine>
@@ -310,7 +310,7 @@
             <plugin>
                 <groupId>org.jacoco</groupId>
                 <artifactId>jacoco-maven-plugin</artifactId>
-                <version>0.8.12</version>
+                <version>0.8.13</version>
                 <executions>
                     <execution>
                         <id>prepare-agent</id>
diff --git a/pom.xml b/pom.xml
index f5f9c19..ec1f6b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <artifactId>bucketfs-client</artifactId>
-    <version>2.2.0</version>
+    <version>2.2.1</version>
     <description>Command line client for BucketFS</description>
     <url>https://github.com/exasol/bucketfs-client/</url>
     <properties>
@@ -42,6 +42,12 @@
                         <configuration>
                             <mainClass>${main.class}</mainClass>
                             <imageName>bfsc-${os.detected.name}_${os.detected.arch}</imageName>
+                            <buildArgs>
+                                <!-- Optimize for compatibility -->
+                                <buildArg>-march=compatibility</buildArg>
+                                <!-- Optimize for size rather than speed -->
+                                <buildArg>-O1</buildArg>
+                            </buildArgs>
                         </configuration>
                     </plugin>
                 </plugins>
@@ -186,7 +192,7 @@
             <plugin>
                 <groupId>com.exasol</groupId>
                 <artifactId>project-keeper-maven-plugin</artifactId>
-                <version>5.0.1</version>
+                <version>5.1.0</version>
                 <executions>
                     <execution>
                         <goals>
@@ -229,7 +235,7 @@
                 <artifactId>ossindex-maven-plugin</artifactId>
                 <configuration>
                     <excludeVulnerabilityIds>
-                        <!-- com.exasol:exasol-jdbc -->
+                        <!-- Version 24.2.1 of the Exasol JDBC driver fixes CVE-2024-55551. But the OSSIndex does not contain fix version yet. -->
                         <exclude>CVE-2024-55551</exclude>
                     </excludeVulnerabilityIds>
                 </configuration>
@@ -239,7 +245,7 @@
     <parent>
         <artifactId>bucketfs-client-generated-parent</artifactId>
         <groupId>com.exasol</groupId>
-        <version>2.2.0</version>
+        <version>2.2.1</version>
         <relativePath>pk_generated_parent.pom</relativePath>
     </parent>
 </project>
diff --git a/src/main/java/com/exasol/bucketfs/url/BucketFsUrl.java b/src/main/java/com/exasol/bucketfs/url/BucketFsUrl.java
index 8e9862b..947f52e 100644
--- a/src/main/java/com/exasol/bucketfs/url/BucketFsUrl.java
+++ b/src/main/java/com/exasol/bucketfs/url/BucketFsUrl.java
@@ -13,8 +13,9 @@
 
 /**
  * BucketFS-specific URL.
- *
+ * <p>
  * For compatibility this class is modeled after {@link java.net.URL}.
+ * </p>
  */
 // [impl->dsn~bucket-fs-url~2]
 public final class BucketFsUrl {
@@ -38,7 +39,7 @@ public static boolean isBucketFsUrl(final URI uri) {
      * @param uri     URI to create the URL from
      * @param profile default values for specific parts of BucketFS URLs
      * @return new instance of {@link BucketFsUrl}
-     * @throws MalformedURLException if BucketFS service or bucket are missing in the path or the path is not an
+     * @throws BucketFsClientException if BucketFS service or bucket are missing in the path, or the path is not an
      *                               absolute path
      */
     public static BucketFsUrl from(final URI uri, final Profile profile) {
@@ -51,8 +52,7 @@ public static BucketFsUrl from(final URI uri, final Profile profile) {
             throw new BucketFsClientException(ExaError.messageBuilder("E-BFSC-5") //
                     .message("Invalid BucketFS URL: {{url}}.", uri) //
                     .message(" Cause: {{cause}}.", exception.getMessage()) //
-                    .mitigation("Please use URL with the following form: {{form}}.",
-                            "bfs://<bucketfs-service/<bucket>/<path-in-bucket>") //
+                    .mitigation("Please use URL with the following form: 'bfs[s]://<bucketfs-service>/<bucket>/[<path-in-bucket>]'.") //
                     .toString());
         }
     }
@@ -85,8 +85,8 @@ private static BucketFsUrl from(final BucketFsProtocol protocol, final String ho
      * 
      * @param protocol     protocol of this URL, one of
      *                     <ul>
-     *                     <li>{@link #BUCKETFS_PROTOCOL}</li>
-     *                     <li>{@link #BUCKETFS_PROTOCOL_WITH_TLS}</li>
+     *                     <li>{@link com.exasol.bucketfs.url.BucketFsProtocol#BFS}</li>
+     *                     <li>{@link com.exasol.bucketfs.url.BucketFsProtocol#BFSS}</li>
      *                     </ul>
      * @param host         host of this URL
      * @param port         port number, or -1 if the port is not set
@@ -95,7 +95,7 @@ private static BucketFsUrl from(final BucketFsProtocol protocol, final String ho
     BucketFsUrl(final BucketFsProtocol protocol, final String host, final int port, final BucketFsPath bucketFsPath) {
         this.protocol = requireNonNull(protocol, "protocol");
         this.host = requireNonNull(host, "host");
-        this.port = requireNonNull(port, "port");
+        this.port = port;
         this.bucketFsPath = requireNonNull(bucketFsPath, "bucketFsPath");
     }
 
diff --git a/src/test/java/com/exasol/bucketfs/ProcessExecutor.java b/src/test/java/com/exasol/bucketfs/ProcessExecutor.java
index eb86f96..dcc175e 100644
--- a/src/test/java/com/exasol/bucketfs/ProcessExecutor.java
+++ b/src/test/java/com/exasol/bucketfs/ProcessExecutor.java
@@ -21,7 +21,7 @@ public class ProcessExecutor {
     private static final Duration TIMEOUT = Duration.ofSeconds(5);
 
     /** Name of the JAR file */
-    public static final String JAR_NAME = "bfsc-2.2.0.jar";
+    public static final String JAR_NAME = "bfsc-2.2.1.jar";
 
     /**
      * Create a {@link ProcessExecutor} for the jar built by for the current projects. The Jar file must be built before


Reviews:
user: login: kaklakariada
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-08T05:57:59Z
user: login: kaklakariada
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-09T11:43:10Z
user: login: kaklakariada
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T11:45:20Z
user: login: redcatbear
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T12:51:24Z
user: login: kaklakariada
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-09T13:44:58Z
user: login: kaklakariada
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T12:09:16Z
user: login: kaklakariada
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-13T12:39:32Z

Review comments:
diff_hunk: @@ -130,6 +139,20 @@ ReadPasswd = <value>
 
 Please note that each of the passwords is base64-encoded. So before providing the passwords to BFSC please apply `echo <password> | base64 -d`.
 
+#### Variant 2: Reading the Password Using `confd_client` (Recommended)
+
+You can get the configuration of a BucketFS service with the `confd_client` tool (see also ["bucketfs_info"](https://docs.exasol.com/db/latest/confd/jobs/bucketfs_info.htm)):
+
+```shell
+confd_client bucketfs_info bucketfs_name: <bucketfs-service-name>
+```
+
+Example:
+
+```shell
+confd_client bucketfs_info bucketfs_name: bucketfs1
+```
+
path: doc/user_guide/user_guide.md
user: login: kaklakariada
body: As this is recommended, move it up and name it "Variant 1"
created_at: 2025-05-08T05:57:32Z
author_association: COLLABORATOR
diff_hunk: @@ -42,6 +42,14 @@
                         <configuration>
                             <mainClass>${main.class}</mainClass>
                             <imageName>bfsc-${os.detected.name}_${os.detected.arch}</imageName>
+                            <buildArgs>
+                                <!-- Optimize for compatibility -->
+                                <buildArg>-march=compatibility</buildArg>
+                                <!-- Optimize for size rather than speed -->
+                                <buildArg>-O1</buildArg>
+                                <!-- Build a static library with the exception of LibC -->
+                                <buildArg>--static-nolibc</buildArg>
path: pom.xml
user: login: kaklakariada
body: Build fails with `Unrecognized option: --static-nolibc`. Maybe we need to use a newer GraalVM instead of 17?
created_at: 2025-05-09T11:45:15Z
author_association: COLLABORATOR
diff_hunk: @@ -42,6 +42,14 @@
                         <configuration>
                             <mainClass>${main.class}</mainClass>
                             <imageName>bfsc-${os.detected.name}_${os.detected.arch}</imageName>
+                            <buildArgs>
+                                <!-- Optimize for compatibility -->
+                                <buildArg>-march=compatibility</buildArg>
+                                <!-- Optimize for size rather than speed -->
+                                <buildArg>-O1</buildArg>
+                                <!-- Build a static library with the exception of LibC -->
+                                <buildArg>--static-nolibc</buildArg>
path: pom.xml
user: login: redcatbear
body: Removed option. I will try the binaries under Ubuntu and Fedora. If they run there, that should be fine.
created_at: 2025-05-09T12:51:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -138,65 +163,44 @@ If the BucketFS server uses a self-signed certificate or the certificate is not
 
 ### Configuration File
 
-Besides specifying the complete URL on the command line you can use defaults for some parts of the URL.
-
-BFSC uses the following precedence for URI parts
-1. Supplied on the command line, or entered interactively in case of a [password](#password-protected-bucket-access)
-2. Data in your profile
-3. Hard coded default value for host and port
-
 You can define the profile in BFSC's configuration file in your home directory: `~/.bucketfs-client/config.ini`. On Windows the home directory is `%USERPROFILE%`.
 
-The configuration file uses the INI-file syntax. An INI file is divided into sections. Each section contains a number of lines defining a *profile*. The first line of each section specifies the name of the profile in brackets. The default profile's name is `default`. Each of the following lines of the section may assign a value to a variable.
+The configuration file uses the INI-file syntax. An INI file is divided into sections. Each section contains a number of lines defining a *profile*. The first line of each section specifies the name of the profile in brackets. The default profile's name is `default`. Each line after the section header assigns a value to a variable.
 
-BFCS uses the following elements of your profile with the specified hard coded default values:
+BFSC uses the following elements of your profile with the specified hard-coded default values:
 
 | Parameter                       | Variable         | Default value                   |
 |---------------------------------|------------------|---------------------------------|
-| Protocol of BucketFS server     | `protocol`       | `bfs` or `bfss`, default: `bfs` |
-| Host address of BucketFS server | `host`           | `localhost`                     |
-| Port                            | `port`           | `2580`                          |
 | Name of root bucket             | `bucket`         | (none)                          |
 | Password for read operations    | `password.read`  | (none)                          |
 | Password for write operations   | `password.write` | (none)                          |
 | Path to TLS certificate         | `certificate`    | (none)                          |
 
-Here is an example for the content of a configuration file for BFSC:
+Here is an example content of a configuration file for BFSC:
 ```
 [default]
-protocol=bfss
-host=1.2.3.4
-port=8888
 bucket=simba
 password.read=abc
 password.write=def
 certificate=/path/to/cert.crt
 ```
 
-Using this configuration file the command line
-```shell
-bfsc cp foo.jar bfs://1.2.3.4:8888/simba/drivers/foo.jar
-```
-... can then be abbreviated to:
-```shell
-bfsc cp foo.jar bfs:/drivers
-```
-
 ### Name of the Root Bucket
 
 BFSC cannot detect whether the URL on the command line contains a bucket name or directly starts with the path *inside* the bucket. If the profile in your configuration file specifies a bucket name, then BFSC will always prepend that bucket name to the path of the URL.
 
 If you want to provide the name of the root bucket on the command line then please do not set `bucket` in your profile.
 
-In case your profile specifes the name of the root bucket all of the following commands will list the contents of directory `folder` in the root bucket:
+In case your profile specifies the name of the root bucket all following commands will list the contents of directory `folder` in the root bucket:
+
 ```shell
 bfsc ls folder
 bfsc ls /folder
 bfsc ls bfs:///folder
 bfsc ls bfs:/folder
path: doc/user_guide/user_guide.md
user: login: kaklakariada
body: You might also want to remove these examples for abbreviated URLs
created_at: 2025-05-13T11:56:05Z
author_association: COLLABORATOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-13T08:59:06Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=com.exasol%3Abucketfs-client&pullRequest=53) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=com.exasol%3Abucketfs-client&pullRequest=53&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=com.exasol%3Abucketfs-client&pullRequest=53&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=com.exasol%3Abucketfs-client&pullRequest=53&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=com.exasol%3Abucketfs-client&pullRequest=53&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=com.exasol%3Abucketfs-client&pullRequest=53&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=com.exasol%3Abucketfs-client&pullRequest=53)


author_association: NONE