Server

This chapter is aimed solely at system admins. It contains instructions on how to get the BOMnipotent server up and running, and how to configure it to your needs.

Mar 9, 2025

Subsections of Server

Subsections of Initial Setup

Starting the Server

Several setup variants are presented here. You can pick the one best suited to your needs, and modify it at will.

  • The setup via docker compose is probably the easiest, and directly exposes your server to the internet. Choose this method if you have a dedicated system and IP just for BOMnipotent Server.
  • The setup including a proxy is very similar, but also sets up a reverse proxy. Choose this method if you potentially want to offer more than one service from the same system and IP.
  • The standalone setup avoids the overhead of containerisation, at the cost of, well, containerisation. Choose this method if you are experienced in more classical server setups.

After following the steps of one of the variants, your server has a default configuration and should be reachable from the internet. After that, you should create an admin user account .

May 12, 2025

Subsections of Starting the Server

Docker Compose

The recommended and easiest setup for BOMnipotent Server uses docker compose . This variant of the setup will make BOMnipotent Server directly reachable from the internet. If you want to handle traffic through a reverse proxy, check out another setup instead.

Suggested File Structure

The suggested file structure in the favourite directory of your server looks like this:

├── .env
├── bomnipotent_config
│   ├── config.toml
│   └── config.toml.default
└── compose.yaml

This tutorial will walk through the files and explain them one by one.

.env

BOMnipotent server communicates with a database. Currently, only PostgreSQL is supported as a backend. The database is protected by a password. It is best practice to store the password inside a separate .env file instead of directly in the compose.yaml.

The name of the file must be “.env”, otherwise docker will not recognise it.

Your .env file should look like this:

BOMNIPOTENT_DB_PW=<your-database-password>
SMTP_SECRET=<your-smtp-authentication-secret>

If you are using a versioning system to store your setup, do not forget to add “.env” to your .gitignore or analogous ignore file!

config.toml

BOMnipotent Server needs a configuration file, which is explained in more detail in another section .

The name of the file is arbitrary in principle, but the ready-to-deploy BOMnipotent Server docker container is set up to look for “config.toml”.

A minimal configuration looks like this:

# The db_url has the structure [db_client]://[user]:[password]@[container]:[port]/[db]
# Note that ${BOMNIPOTENT_DB_PW} references an environment variable.
db_url = "postgres://bomnipotent_user:${BOMNIPOTENT_DB_PW}@bomnipotent_db:5432/bomnipotent_db"
# Domain behind which bomnipotent server will be hosted
domain = "https://bomnipotent.<your-domain>.<top-level>"

[tls]
# The path to your full TLS certificate chain
certificate_chain_path = "/etc/ssl/certs/<your-TLS-certificate-chain.crt>"
# The path to your secret TLS key
secret_key_path = "/etc/ssl/private/<your-secret-TLS-key>"

[smtp]
# The username for your mail provider, typically your mail address
user = "<you@yourdomain.com>"
# The smtp endpoint of your mail provider
endpoint = "<your.smtp.host>"
# The secret to authenticate against the mail provider, typically your password
secret = "${SMTP_SECRET}"

# Publisher data according to the CSAF Standard linked below
[provider_metadata.publisher]
name = "<Provide the name of your organsiation>"
# Namespace of your organisation, in form of a complete URL
namespace = "https://<your-domain>.<top-level>"
# This is most likely the enum variant you want
category = "vendor"
# Contact details are optional and in free form
contact_details = "<For security inquiries, please contact us at...>"

Fill in the braces with your data.

The section about TLS configuration contains more detailed information to avoid common pitfalls.

If you prefer using a local smtp releay station, have a look at the necessary adjustments to the compose file.

The publisher data is used to comply with the OASIS CSAF standard .

The section about provider-metadata goes into more details what the fields actually mean.

It is recommended to store your config.toml file inside a dedicated directory, “bomnipotent_config” in this example. The docker compose file will grant read access to this folder. This setup has two advantages:

  • In the unlikely case of a security breach of the BOMnipotent Server container, an attacker would only have access to you config directory, and nothing else on your server.
  • BOMnipotent Server will watch the directory for changes and will try to reload the configuration file if it has changed. This does not work when exposing only a single file to the docker container.

Many configuration values support hot reloading, meaning they can be modified without restarting the server.

After having set up your config.toml, you may want to copy it as for example config.toml.default, to be able to quickly restore your initial configuration. This is entirely optional, though.

compose.yaml

The compose file is where you specify the container setup. Once it is running smoothly, it does not need to be modified very often, but initially understanding it can take some time if you are new to docker.

The file needs to be called “compose.yaml”, docker can be a bit pecky otherwise.

A completely ready to deploy compose file looks like this:

# Giving the setup a name is optional, it will be derived by docker otherwise.
name: bomnipotent_server_containers

# The docker containers need to communicate, and they need a network for that.
networks:
  # This network needs a reference
  bomnipotent_network:
    # Since the containers are on the same docker host, "bridge" is a reasonable driver choice.
    driver: bridge
    # Giving the network the same name as the reference is ok.
    name: bomnipotent_network

volumes:
  # Define the volume for persistent storage of the database
  bomnipotent_data:
    driver: local
  # The server itself also needs persistence if you do not want to activate the subscription after every reboot
  bomnipotent_subscription:
    driver: local

services:
  bomnipotent_db:
    # Name of the database container
    container_name: bomnipotent_db
    deploy:
      resources:
        limits:
          # Limit the CPU usage to 0.5 cores
          cpus: "0.5"
          # Limit the memory usage to 512MB
          memory: "512M"
    environment:
      # Set the database name
      POSTGRES_DB: bomnipotent_db
      # Set the database user
      POSTGRES_USER: bomnipotent_user
      # Set the database password from the .env file variable
      POSTGRES_PASSWORD: ${BOMNIPOTENT_DB_PW}
    healthcheck:
      # Check if the database is ready
      test: ["CMD-SHELL", "pg_isready -U bomnipotent_user -d bomnipotent_db"]
      # Interval between health checks
      interval: 60s
      # Timeout for each health check
      timeout: 10s
      # Number of retries before considering the container unhealthy
      retries: 5
      # Start period before the first health check
      start_period: 10s
    # Use the specified PostgreSQL image
    # You may ddjust the container tag at will
    image: postgres:17
    logging:
      # Use the local logging driver
      driver: local
      options:
        # Limit the log size to 10MB
        max-size: "10m"
        # Keep a maximum of 3 log files
        max-file: "3"
    networks:
      # Connect to the specified network
      - bomnipotent_network
    # Restart the container if it has stopped for some reason other than a user command
    restart: always
    volumes:
      # Mount the volume for persistent data storage
      - bomnipotent_data:/var/lib/postgresql/data

  bomnipotent_server:
    # Name of the server container
    container_name: bomnipotent_server
    depends_on:
      # Ensure the database service is healthy before starting the server
      bomnipotent_db:
        condition: service_healthy
    deploy:
      resources:
        limits:
          # Limit the CPU usage to 0.5 cores
          cpus: "0.5"
          # Limit the memory usage to 512MB
          memory: "512M"
    environment:
      # Pass the database password on to the server.
      BOMNIPOTENT_DB_PW: ${BOMNIPOTENT_DB_PW}
      # Pass the SMTP secret on to the server.
      SMTP_SECRET: ${SMTP_SECRET}
    healthcheck:
      # Check if the server is healthy
      # Your TLS certificate is most likely not valid for "localhost"
      # Hence the --insecure flag
      test: ["CMD-SHELL", "curl --fail --insecure https://localhost:8443/health || exit 1"]
      # Interval between health checks
      interval: 60s
      # Timeout for each health check
      timeout: 10s
      # Number of retries before considering the container unhealthy
      retries: 5
      # Start period before the first health check
      start_period: 10s
    # This is the official docker image running a BOMnipotent Server instance.
    image: wwhsoft/bomnipotent_server:latest
    logging:
      # Use the local logging driver
      driver: local
      options:
        # Limit the log size to 10MB
        max-size: "10m"
        # Keep a maximum of 3 log files
        max-file: "3"
    networks:
      # Connect to the specified network
      - bomnipotent_network
    ports:
      # Map port 443 on the host to port 8443 on the container
      # This allows to connect to it via encrypted communication from the internet
      - target: 8443
        published: 443
    # Restart the container if it has stopped for some reason other than a user command
    restart: always
    volumes:
      # Bind mount the config folder on the host
      - type: bind
        source: ./bomnipotent_config
        target: /etc/bomnipotent_server/configs/
        read_only: true
      # Bind mount the SSL directory, so that BOMnipotent can find the TLS certificate and key
      - type: bind
        source: /etc/ssl
        target: /etc/ssl
        read_only: true
      # The subscription can be stored inside the container
      - bomnipotent_subscription:/root/.config/bomnipotent
name: bomnipotent_server_containers

networks:
  bomnipotent_network:
    driver: bridge
    name: bomnipotent_network

volumes:
  bomnipotent_data:
    driver: local
  bomnipotent_subscription:
    driver: local

services:
  bomnipotent_db:
    container_name: bomnipotent_db
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: "512M"
    environment:
      POSTGRES_DB: bomnipotent_db
      POSTGRES_USER: bomnipotent_user
      POSTGRES_PASSWORD: ${BOMNIPOTENT_DB_PW}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U bomnipotent_user -d bomnipotent_db"]
      interval: 60s
      timeout: 10s
      retries: 5
      start_period: 10s
    image: postgres:17
    logging:
      driver: local
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - bomnipotent_network
    restart: always
    volumes:
      - bomnipotent_data:/var/lib/postgresql/data

  bomnipotent_server:
    container_name: bomnipotent_server
    depends_on:
      bomnipotent_db:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: "512M"
    environment:
      BOMNIPOTENT_DB_PW: ${BOMNIPOTENT_DB_PW}
      SMTP_SECRET: ${SMTP_SECRET}
    healthcheck:
      test: ["CMD-SHELL", "curl --fail --insecure https://localhost:8443/health || exit 1"]
      interval: 60s
      timeout: 10s
      retries: 5
      start_period: 10s
    image: wwhsoft/bomnipotent_server:latest
    logging:
      driver: local
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - bomnipotent_network
    ports:
      - target: 8443
        published: 443
    restart: always
    volumes:
      - type: bind
        source: ./bomnipotent_config
        target: /etc/bomnipotent_server/configs/
        read_only: true
      - type: bind
        source: /etc/ssl
        target: /etc/ssl
        read_only: true
      - bomnipotent_subscription:/root/.config/bomnipotent

Store this as “compose.yaml”. Then, call:

docker compose --detach
docker compose -d

Your server is now up and running!

Run “docker ps” to check if it is healthy.

May 23, 2025

Docker Compose incl. Reverse Proxy

This variant of the very similar setup with docker compose not only sets up a running BOMnipotent Server, but also an nginx reverse proxy.

Suggested File Structure

The suggested file structure in the favourite directory of your server looks like this:

├── .env
├── bomnipotent_config
│   ├── config.toml
│   └── config.toml.default
├── proxy_config
│   └── conf.d
│       └── default.conf
└── compose.yaml

This tutorial will walk through the files and explain them one by one.

proxy_config/conf.d/default.conf

The use of nginx as the reverse proxy is merely a suggestion. You can substitute it with any other server software you prefer.

In very crude terms, the reverse proxy serves as a gateway to your server: It allows you to host several services (BOMnipotent Server, a website, etc.) behind the same IP address. Any request to one of your URLs will end up at the reverse proxy, which then passes them on to the correct service. This is how you land on a different website when you visit doc.bomnipotent.de than when you visit www.bomnipotent.de , although they are hosted behind the same IP address.

Nginx looks up its configuration in various locations. Later on in the compose.yaml we will use mount binding to sneakily inject our configuration into the nginx docker container.

You can use the following as the starting point for your default.conf:

# Rate limiting: Allows up to 5 requests per second per IP address, stored in a memory zone of 10 MB.
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;

# BOMnipotent Server
server {
    # This makes the server listen on port 443, which is typically used for HTTPS.
    listen 443 ssl http2;
    # Replace this with the actual domain of your BOMnipotent Server.
    server_name bomnipotent.your-domain.com;

    # Replace this with the actual certificate for your domain.
    ssl_certificate /etc/ssl/certs/your-domain-fullchain.crt;
    # Replace this with the actual private key for your certificate.
    ssl_certificate_key /etc/ssl/private/your-domain_private_key.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384";

    location / {
        # Apply rate limiting
        limit_req zone=api_limit burst=10 nodelay;

        # This tells nginx to pass on requests to port 8080 of the docker container.
        proxy_pass http://bomnipotent_server:8080;
        proxy_set_header Host $host;
        # The following lines assure that the BOMnipotent logs contain the IP of the sender,
        # instead of the local IP of the reverse proxy.
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

You probably want to add more “server” blocks – why else would you decide to set up a reverse proxy?

.env

BOMnipotent server communicates with a database. Currently, only PostgreSQL is supported as a backend. The database is protected by a password. It is best practice to store the password inside a separate .env file instead of directly in the compose.yaml.

The name of the file must be “.env”, otherwise docker will not recognise it.

Your .env file should look like this:

BOMNIPOTENT_DB_PW=<your-database-password>
SMTP_SECRET=<your-smtp-authentication-secret>

If you are using a versioning system to store your setup, do not forget to add “.env” to your .gitignore or analogous ignore file!

config.toml

BOMnipotent Server needs a configuration file, which is explained in more detail in another section .

The name of the file is arbitrary in principle, but the ready-to-deploy BOMnipotent Server docker container is set up to look for “config.toml”.

A minimal configuration for a BOMnipotent Server behind a reverse proxy looks like this:

# The db_url has the structure [db_client]://[user]:[password]@[container]:[port]/[db]
# Note that ${BOMNIPOTENT_DB_PW} references an environment variable.
db_url = "postgres://bomnipotent_user:${BOMNIPOTENT_DB_PW}@bomnipotent_db:5432/bomnipotent_db"
# Domain behind which bomnipotent server will be hosted
domain = "https://bomnipotent.<your-domain>.<top-level>"

[tls]
# TLS encryption is handled by the reverse proxy,
# BOMnipotent Server is not directly reachable from the internet.
allow_http = true

[smtp]
# The username for your mail provider, typically your mail address
user = "<you@yourdomain.com>"
# The smtp endpoint of your mail provider
endpoint = "<your.smtp.host>"
# The secret to authenticate against the mail provider, typically your password
secret = "${SMTP_SECRET}"

# Publisher data according to the CSAF Standard linked below
[provider_metadata.publisher]
name = "<Provide the name of your organsiation>"
# Namespace of your organisation, in form of a complete URL
namespace = "https://<your-domain>.<top-level>"
# This is most likely the enum variant you want
category = "vendor"
# Contact details are optional and in free form
contact_details = "<For security inquiries, please contact us at...>"

Fill in the braces with your data.

If you prefer using a local smtp releay station, have a look at the necessary adjustments to the compose file.

The publisher data is used to comply with the OASIS CSAF standard .

The section about provider-metadata goes into more details what the fields actually mean.

It is recommended to store your config.toml file inside a dedicated directory, “bomnipotent_config” in this example. The docker compose file will grant read access to this folder. This setup has two advantages:

  • In the unlikely case of a security breach of the BOMnipotent Server container, an attacker would only have access to you config directory, and nothing else on your server.
  • BOMnipotent Server will watch the directory for changes and will try to reload the configuration file if it has changed. This does not work when exposing only a single file to the docker container.

Many configuration values support hot reloading, meaning they can be modified without restarting the server.

After having set up your config.toml, you may want to copy it as for example config.toml.default, to be able to quickly restore your initial configuration. This is entirely optional, though.

compose.yaml

The compose file is where you specify the container setup. Once it is running smoothly, it does not need to be modified very often, but initially understanding it can take some time if you are new to docker.

The file needs to be called “compose.yaml”, docker can be a bit pecky otherwise.

A completely ready to deploy compose file looks like this:

# Giving the setup a name is optional, it will be derived by docker otherwise.
name: bomnipotent_server_containers

# The docker containers need to communicate, and they need a network for that.
networks:
  # This network needs a reference
  bomnipotent_network:
    # Since the containers are on the same docker host, "bridge" is a reasonable driver choice.
    driver: bridge
    # Giving the network the same name as the reference is ok.
    name: bomnipotent_network
  # The reverse proxy needs to communicate with BOMnipotent Server, but not with the database.
  proxy_network:
    driver: bridge
    name: proxy_network

volumes:
  # Define the volume for persistent storage of the database
  bomnipotent_data:
    driver: local
  # The server itself also needs persistence if you do not want to activate the subscription after every reboot
  bomnipotent_subscription:
    driver: local

services:
  reverse_proxy:
    # Name of the reverse proxy container
    container_name: reverse_proxy
    deploy:
      resources:
        limits:
          # Limit the CPU usage to 0.5 cores
          cpus: "0.5"
          # Limit the memory usage to 512MB
          memory: "512M"
    healthcheck:
      # Check if nginx is running and could parse the config.
      test: ["CMD-SHELL", "nginx -t || exit 1"]
      # Interval between health checks
      interval: 60s
      # Timeout for each health check
      timeout: 10s
      # Number of retries before considering the container unhealthy
      retries: 3
      # Start period before the first health check
      start_period: 60s
    image: nginx:latest
    logging:
      # Use the local logging driver
      driver: local
      options:
        # Limit the log size to 10MB
        max-size: "10m"
        # Keep a maximum of 3 log files
        max-file: "3"
    networks:
      # Connect to the specified network
      - proxy_network
    ports:
      # Expose port 443 of the container
      # This allows to connect to it via encrypted communication from the internet
      - "443:443"
    # Restart the container if it has stopped for some reason other than a user command
    restart: on-failure
    volumes:
      # Bind mount the SSL directory, so that nginx can find the TLS certificate and key
      - type: bind
        source: /etc/ssl
        target: /etc/ssl
        read_only: true
      # Bind mount the config folder on the host
      - type: bind
        source: ./proxy_config/conf.d
        target: /etc/nginx/conf.d
        read_only: true

  bomnipotent_db:
    # Name of the database container
    container_name: bomnipotent_db
    deploy:
      resources:
        limits:
          # Limit the CPU usage to 0.5 cores
          cpus: "0.5"
          # Limit the memory usage to 512MB
          memory: "512M"
    environment:
      # Set the database name
      POSTGRES_DB: bomnipotent_db
      # Set the database user
      POSTGRES_USER: bomnipotent_user
      # Set the database password from the .env file variable
      POSTGRES_PASSWORD: ${BOMNIPOTENT_DB_PW}
    healthcheck:
      # Check if the database is ready
      test: ["CMD-SHELL", "pg_isready -U bomnipotent_user -d bomnipotent_db"]
      # Interval between health checks
      interval: 60s
      # Timeout for each health check
      timeout: 10s
      # Number of retries before considering the container unhealthy
      retries: 5
      # Start period before the first health check
      start_period: 10s
    # Use the specified PostgreSQL image
    # You may ddjust the container tag at will
    image: postgres:17
    logging:
      # Use the local logging driver
      driver: local
      options:
        # Limit the log size to 10MB
        max-size: "10m"
        # Keep a maximum of 3 log files
        max-file: "3"
    networks:
      # Connect to the specified network
      - bomnipotent_network
    # Restart the container if it has stopped for some reason other than a user command
    restart: always
    volumes:
      # Mount the volume for persistent data storage
      - bomnipotent_data:/var/lib/postgresql/data

  bomnipotent_server:
    # Name of the server container
    container_name: bomnipotent_server
    depends_on:
      # Ensure the database service is healthy before starting the server
      bomnipotent_db:
        condition: service_healthy
    deploy:
      resources:
        limits:
          # Limit the CPU usage to 0.5 cores
          cpus: "0.5"
          # Limit the memory usage to 512MB
          memory: "512M"
    environment:
      # Pass the database password on to the server.
      BOMNIPOTENT_DB_PW: ${BOMNIPOTENT_DB_PW}
      # Pass the SMTP secret on to the server.
      SMTP_SECRET: ${SMTP_SECRET}
    healthcheck:
      # Check if the server is healthy
      test: ["CMD-SHELL", "curl --fail http://localhost:8080/health || exit 1"]
      # Interval between health checks
      interval: 60s
      # Timeout for each health check
      timeout: 10s
      # Number of retries before considering the container unhealthy
      retries: 5
      # Start period before the first health check
      start_period: 10s
    # This is the official docker image running a BOMnipotent Server instance.
    image: wwhsoft/bomnipotent_server:latest
    logging:
      # Use the local logging driver
      driver: local
      options:
        # Limit the log size to 10MB
        max-size: "10m"
        # Keep a maximum of 3 log files
        max-file: "3"
    networks:
      # Connect the server to the reverse proxy
      - proxy_network
      # Connect the server to the database
      - bomnipotent_network
    # Restart the container if it has stopped for some reason other than a user command
    restart: always
    volumes:
      # Bind mount the config folder on the host
      - type: bind
        source: ./bomnipotent_config
        target: /etc/bomnipotent_server/configs/
        read_only: true
      # The subscription can be stored inside the container
      - bomnipotent_subscription:/root/.config/bomnipotent
name: bomnipotent_server_containers

networks:
  bomnipotent_network:
    driver: bridge
    name: bomnipotent_network
  proxy_network:
    driver: bridge
    name: proxy_network

volumes:
  bomnipotent_data:
    driver: local
  bomnipotent_subscription:
    driver: local

services:
  reverse_proxy:
    container_name: reverse_proxy
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: "512M"
    healthcheck:
      test: ["CMD-SHELL", "nginx -t || exit 1"]
      interval: 60s
      timeout: 10s
      retries: 3
      start_period: 60s
    image: nginx:latest
    logging:
      driver: local
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - proxy_network
    ports:
      - "443:443"
    restart: on-failure
    volumes:
      - type: bind
        source: /etc/ssl
        target: /etc/ssl
        read_only: true
      - type: bind
        source: ./proxy_config/conf.d
        target: /etc/nginx/conf.d
        read_only: true

  bomnipotent_db:
    container_name: bomnipotent_db
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: "512M"
    environment:
      POSTGRES_DB: bomnipotent_db
      POSTGRES_USER: bomnipotent_user
      POSTGRES_PASSWORD: ${BOMNIPOTENT_DB_PW}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U bomnipotent_user -d bomnipotent_db"]
      interval: 60s
      timeout: 10s
      retries: 5
      start_period: 10s
    image: postgres:17
    logging:
      driver: local
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - bomnipotent_network
    restart: always
    volumes:
      - bomnipotent_data:/var/lib/postgresql/data

  bomnipotent_server:
    container_name: bomnipotent_server
    depends_on:
      bomnipotent_db:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: "512M"
    environment:
      BOMNIPOTENT_DB_PW: ${BOMNIPOTENT_DB_PW}
      SMTP_SECRET: ${SMTP_SECRET}
    healthcheck:
      test: ["CMD-SHELL", "curl --fail http://localhost:8080/health || exit 1"]
      interval: 60s
      timeout: 10s
      retries: 5
      start_period: 10s
    image: wwhsoft/bomnipotent_server:latest
    logging:
      driver: local
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - proxy_network
      - bomnipotent_network
    restart: always
    volumes:
      - type: bind
        source: ./bomnipotent_config
        target: /etc/bomnipotent_server/configs/
        read_only: true
      - bomnipotent_subscription:/root/.config/bomnipotent

Store this as “compose.yaml”. Then, call:

docker compose --detach
docker compose -d

Your server is now up and running!

Run “docker ps” to check if it is healthy.

May 23, 2025

Standalone

You are not required to run the official BOMnipotent Server docker container . Instead, you can download and directly run the BOMnipotent Server binary as a standalone application.

This setup only really makes sense for version 0.4.2 onwards, because the server did not offer port adjustments and logging to files before that.

Prerequisite: PostgreSQL

BOMnipotent Server requires a PostgreSQL database for storing its data. The setup depends on your server operating system.

On the most common distributions, PostgreSQL is offered as a package. You can install it via for example apt/apt-get/aptitude:

sudo apt-get install postgresql postgresql-contrib

Now PostgeSQL is running as a service. To further modify it, you need to become the postgres system user:

sudo -i -u postgres

Now you can open the interactive PostgreSQL shell:

psql

Inside the shell, you need to add the user “bomnipotent_user”, a password, and create a database “bomnipotent_db”:

CREATE USER bomnipotent_user WITH PASSWORD 'your-password';
CREATE DATABASE bomnipotent_db OWNER bomnipotent_user;
GRANT ALL PRIVILEGES ON DATABASE bomnipotent_db TO bomnipotent_user;
\q

You could use different names for the user and database, but would need to adjust the “db_url” entry of your config file accordingly.

Finally, restart PostgreSQL and switch back to your regular user:

sudo systemctl restart postgresql;
exit

For Windows, PostgreSQL offers an interactive installer .

After it has completed, PostgeSQL is running as a service. Open an interactive PostgreSQL shell by starting an admin console and prompting:

psql -U postgres

Inside the shell, you need to add the user “bomnipotent_user”, a password, and create a database “bomnipotent_db”:

CREATE USER bomnipotent_user WITH PASSWORD 'your-password';
CREATE DATABASE bomnipotent_db OWNER bomnipotent_user;
GRANT ALL PRIVILEGES ON DATABASE bomnipotent_db TO bomnipotent_user;
\q

You could use different names for the user and database, but would need to adjust the “db_url” entry of your config file accordingly.

If you only want to run BOMnipotent Server as a standalone application, but still want to containerise PostgreSQL, you can spin it up via:

docker run --name bomnipotent_db \
  -e POSTGRES_DB=bomnipotent_db \
  -e POSTGRES_USER=bomnipotent_user \
  -e POSTGRES_PASSWORD=<your-password> \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  -d postgres:latest

This creates a container named “bomnipotent_db”, with a database also called “bomnipotent_db”, a user called “bomnipotent_user”, and a password that you need to set. It exposes port 5432 of the container, persists the data in a docker volume, and spins up the “postgres:latest” image in detached mode.

You could use different names for the user and database, but would need to adjust the “db_url” entry of your config file accordingly.

Suggested File Structure

The suggested file structure in the favourite directory of your server looks like this:

├── bomnipotent_config
│   ├── .env
│   ├── config.toml
│   └── config.toml.default
└── bomnipotent_server

This tutorial will walk through the files and explain them one by one.

.env

BOMnipotent server communicates with a database. Currently, only PostgreSQL is supported as a backend. The database is protected by a password. It is best practice to store the password inside a separate .env file instead of directly in the compose.yaml.

The name of the file must be “.env”, otherwise BOMnipotent Server will not recognise it.

Your .env file should look like this:

BOMNIPOTENT_DB_PW=<your-database-password>
SMTP_SECRET=<your-smtp-authentication-secret>

If you are using a versioning system to store your setup, do not forget to add “.env” to your .gitignore or analogous ignore file!

config.toml

BOMnipotent Server needs a configuration file, which is explained in more detail in another section .

The name of the file is arbitrary.

A minimal configuration looks like this:

# The db_url has the structure [db_client]://[user]:[password]@[address]:[port]/[db]
# Note that ${BOMNIPOTENT_DB_PW} references an environment variable.
db_url = "postgres://bomnipotent_user:${BOMNIPOTENT_DB_PW}@localhost:5432/bomnipotent_db"
# Domain behind which bomnipotent server will be hosted
domain = "https://bomnipotent.<your-domain>.<top-level>"
# Bind to the typical port used for HTTPS
https_port = 443

[log]
# Tell the server to log to a file instead of to stdout
file = "/var/log/bomnipotent.log"

[tls]
# The path to your full TLS certificate chain
certificate_chain_path = "/etc/ssl/certs/<your-TLS-certificate-chain.crt>"
# The path to your secret TLS key
secret_key_path = "/etc/ssl/private/<your-secret-TLS-key>"

[smtp]
# The username for your mail provider, typically your mail address
user = "<you@yourdomain.com>"
# The smtp endpoint of your mail provider
endpoint = "<your.smtp.host>"
# The secret to authenticate against the mail provider, typically your password
secret = "${SMTP_SECRET}"

# Publisher data according to the CSAF Standard linked below
[provider_metadata.publisher]
name = "<Provide the name of your organsiation>"
# Namespace of your organisation, in form of a complete URL
namespace = "https://<your-domain>.<top-level>"
# This is most likely the enum variant you want
category = "vendor"
# Contact details are optional and in free form
contact_details = "<For security inquiries, please contact us at...>"

Fill in the braces with your data.

The section about TLS configuration contains more detailed information to avoid common pitfalls.

If you prefer using a local smtp releay station, have a look at the necessary adjustments to the compose file.

The publisher data is used to comply with the OASIS CSAF standard .

The section about provider-metadata goes into more details what the fields actually mean.

It is recommended to store your config.toml file inside a dedicated directory, “bomnipotent_config” in this example. BOMnipotent Server will watch the directory for any changes, meaning that it has less to watch the less files are in the folder. The server will try to reload the configuration file and the .env file if either have changed.

Many configuration values support hot reloading, meaning they can be modified without restarting the server.

After having set up your config.toml, you may want to copy it as for example config.toml.default, to be able to quickly restore your initial configuration. This is entirely optional, though.

bomnipotent_server

This is the binary that runs BOMnipotent Server. You can download any version for your server platform from www.bomnipotent.de .

In principle, all BOMnipotent Server needs to run is to know the path to its config file. If you run the binary and provide the path as the first argument, you have created a working server instance. However, your terminal is now eternally blocked, and the service will not restart if you reboot your system. The rest of this section only serves to ensure that the operating system properly supports the server.

Unlike BOMnipotent Client, which runs under all major operating systems, BOMnipotent Server is currently only supported under Linux and Windows. If you want to host it on a server running MacOS, please create an issue .

This setup requires your distribution to incorporate systemd.

If you’re not sure if it does, it probably does.

If you’re sure that it does not, you probably don’t need these instructions.

Create the file “/etc/systemd/system/bomnipotent.service” with the following content:

[Unit]
Description=BOMnipotent Server
After=network.target

[Service]
ExecStart=/path/to/bomnipotent_server /path/to/bomnipotent_config/config.toml
WorkingDirectory=/path/to/bomnipotent_config
Restart=on-failure
User=<youruser>

[Install]
WantedBy=multi-user.target

Make sure to adjust the values to your specific server. Then run:

sudo systemctl daemon-reexec
sudo systemctl enable --now bomnipotent.service

Open the Task Scheduler (taskschd.msc).

In the right panel, click Create Task.

In the “General” tab:

  • Name the task as “BOMnipotent Server”.
  • Choose “Run whether user is logged on or not”.

In the “Triggers” tab:

  • Click “New” and select “At startup”.

In the “Actions” tab:

  • Click “New” and set the action to “Start a program”.
  • For “Program/script”, select “C:\path\to\bomnipotent_server.exe” (adjust accordingly).
  • In “Add arguments (optional)”, provide “C:\path\to\bomnipotent_config\config.toml” (adjust accordingly).

Click “OK” to save the task.

May 17, 2025

Creating an Admin

Several interactions with BOMnipotent require a user with admin rights. One of these is granting a new user admin rights. This means that some kind of bootstrapping mechanism is required.

Step 1: Create User

First, you will need to create a user account :

bomnipotent_client --domain=<server> user request <your-email>
bomnipotent_client -d <server> user request <your-email>

[INFO] Generating new key pair
[INFO] Storing secret key to "/home/simon/.config/bomnipotent/secret_key.pem" and public key to "/home/simon/.config/bomnipotent/public_key.pem"
[INFO] User request submitted. It now needs to be confirmed by a user manager.

To make things a litle less verbose, let’s store the domain of your server and your email address in a user session :

bomnipotent_client --domain=<server> --email=<your-email> session login
bomnipotent_client -d <server> -e <your-email> session login

[INFO] Storing session data in /home/simon/.config/bomnipotent/session.toml

Step 2: Mark User as TMP Admin

Due to security reasons, the user needs to already exist in the database at this point. Otherwise, a malicious actor could anticipate the email address you use for your admin, and make their own request at an opportune time. To prevent this, the tmp admin mechanism blocks all requests to newly add this particular user to the database.

Next, you will become the user manager that was mentioned in the server reply: Log onto your server machine, and in your server configuration file prepend

tmp_admin = "<your-email>"

It is important to add this line at the beginning of the file, otherwise BOMnipotent might try to interpret this field as part of another section.

Your server logs should now show that the configuration has been reloaded, in addition to the user request you made earlier.

docker logs bomnipotent_server
...
2025-03-06 11:30:15 +00:00 [INFO] Received POST request from 101.102.103.104 to https://bomnipotent.wwh-soft.com/user/info@wwh-soft.com
2025-03-06 11:32:56 +00:00 [INFO] Configuration successfully reloaded from "/etc/bomnipotent_server/configs/config.toml"
...

Step 3: Make User a full Admin

The server now treats authenticated requests from that user as if that user was an admin. To become a permanent admin, you first need to approve your user request. Back on the client, call

bomnipotent_client user approve <your-email>
[INFO] Changed status of info@wwh-soft.com to APPROVED

Now you can make yourself a full server admin:

bomnipotent_client user-role add <your-email> admin
[INFO] Added role to user

Step 4: Remove TMP Admin Mark

The stat of being a temporary admin is intended to be, well, temporary. The server logs a warning whenever you use temporary access rights:

docker logs bomnipotent_server -n 4
2025-03-06 14:51:35 +00:00 [INFO] Received POST request from info@wwh-soft.com to https://bomnipotent.wwh-soft.com/user/info@wwh-soft.com/roles
2025-03-06 14:51:35 +00:00 [WARN] Temporary admin functionality is enabled for info@wwh-soft.com
2025-03-06 14:51:35 +00:00 [INFO] User info@wwh-soft.com was authenticated as a temporary admin
2025-03-06 14:51:35 +00:00 [INFO] Temporary admin info@wwh-soft.com has permission USER_MANAGEMENT to perform this action

But now that you have successfully made yourself a permanent admin, you can and should remove the “tmp_admin” field from the configuration file again.

You are now ready to activate your subscription .

Mar 16, 2025

Activating your Subscription

Most actions that add data to your BOMnipotent database require an active subscription, while reading and removing data do not. This policy ensures that your users do not loose access to the existing data should you one day choose to stop paying for the product.

Commercial entities like companies can acquire a subscription on bomnipotent.de . If you are a non-commercial entity, you can use BOMnipotent without any charge. Request access by sending an email to info@wwh-soft.com .

Shortly after you have acquired a subscription, you will receive an email containing your subscription key.

Subscriptions can only be managed by a user with the “admin” role. Create one if you haven’t already.

Logged in as that user, call

bomnipotent_client subscription activate <your-subscription-key>
[INFO] Successfully stored subscription key.

To check the current status of your subscription, run

bomnipotent_client subscription status
╭──────────┬─────────────┬─────────────────────┬─────────────────────────┬─────────────────────────┬───────────────────────────╮
│ Key      │ Product     │ Subscription Status │ Valid Until             │ Last Updated            │ Assessment                │
├──────────┼─────────────┼─────────────────────┼─────────────────────────┼─────────────────────────┼───────────────────────────┤
│ ***ccfb3 │ BOMnipotent │ active              │ 2025-04-10 17:26:29 UTC │ 2025-03-10 16:26:29 UTC │ The subscription is valid │
│          │             │                     │                         │                         │ .                         │
╰──────────┴─────────────┴─────────────────────┴─────────────────────────┴─────────────────────────┴───────────────────────────╯
Mar 16, 2025

Configuration

This section describes how you can make an instance of BOMnipotent server your own. It introduces the config file , and explains all required and optional parameters it understands.

Mar 9, 2025

Subsections of Configuration

The Config File

Your instance of BOMnipotent Server is configured using a config file. It contains several values, provided in TOML Format . The setup instructions each contain a config file that you can fill with your specific data. All configurations accepted by BOMnipotent Server are described in the rest of this section .

(Re)Loading Configurations

Many configurations support hot reloading. This means that you can change them inside the file, and they take effect without requiring a restart of the server. BOMnipotent Server achieves this by watching for changes of files in the directory that the config file resides in. You can verify a successful reload of the configurations by looking at the logs:

docker logs bomnipotent_server -n 1
2025-03-06 15:34:45 +00:00 [INFO] Configuration successfully reloaded from "/etc/bomnipotent_server/configs/config.toml"

If something does not work as intended, BOMnipotent will also let you know in the logs:

docker logs bomnipotent_server -n 6
2025-03-06 16:11:03 +00:00 [ERROR] Failed to read config file "/etc/bomnipotent_server/configs/config.toml": Failed to parse config file: TOML parse error at line 5, column 1
  |
5 | starlight_throughput = "16 GW"
  | ^^^^^^^^^^^^^^^^^^^^
unknown field `starlight_throughput`, expected one of `allow_http`, `allow_tlp2`, `certificate_chain_path`, `db_url`, `default_tlp`, `domain`, `dos_prevention_limit`, `dos_prevention_period`, `log_level`, `provider_metadata_path`, `publisher_data`, `secret_key_path`, `tmp_admin`, `user_expiration_period`

If the configuration loading produces an error during server startup, the process is aborted. If the configuration cannot be reloaded for an already running server, then the last valid configuration is left untouched.

The official BOMnipotent Server docker image looks for the config file at the path “/etc/bomnipotent_server/configs/config.toml” inside the container. It is recommended to read-only bind the container path “/etc/bomnipotent_server/configs/” to a directory of your choice on the host machine.

Important: For the hot reloading to work, your docker volume must bind to the directory in which the config file is located, not to the file itself. With a direct file binding BOMnipotent will not receive file events and thus cannot reload the config on change.

Environment Variables

Inside your config files, you can reference environment variables. To do so, simply use ${<some-env-var>} anywhere within the file, where “<some-env-var>” is replaced with the name of the variable.

For example, if you provide

BOMNIPOTENT_DB_PW=eHD5B6S8Kze3
export BOMNIPOTENT_DB_PW=eHD5B6S8Kze3
set BOMNIPOTENT_DB_PW=eHD5B6S8Kze3
$env:BOMNIPOTENT_DB_PW = "eHD5B6S8Kze3"
docker run -e BOMNIPOTENT_DB_PW=eHD5B6S8Kze3 wwhsoft/bomnipotent_server --detach
you can use it inside your config.toml like this:

db_url = "postgres://bomnipotent_user:${BOMNIPOTENT_DB_PW}@bomnipotent_db:5432/bomnipotent_db"

You wouldn’t actually use this publicly available example passwort, would you?

BOMnipotent Server supports reading variables from a .env file. If a file with that exact name, “.env”, is located next to your config file, the server will try to evaluate it before loading the config.

Changing the .env file while BOMnipotent Server is running will trigger a hot reloading and a re-evaluation of both the .env and the config file.

Mar 16, 2025

Required Configs

This section explains all parameters which you need to set in order for BOMnipotent Server to start. This required data is specific to you and your setup, which is why it is impossible to assume a meaningful default here.

Mar 9, 2025

Subsections of Required Configs

Database URL

The “db_url” configuration does not support hot reloading. You will need to restart the server after modifying it.

BOMnipotent Server is your gateway for providing supply chain security data and managing access to it. The data itself is stored in a SQL database.

At the moment, only PostgreSQL is supported as a driver.

This database can in principle run in the same environment, in a different container, or on a remote server. BOMnipotent needs to be taught how to reach it. The parameter for providing this information in the config file is “db_url”, and the syntax is the following:

db_url = "<driver>://<user>:<password>@<domain>:<port>/<database>"

BOMnipotent will be cross with you if the string you provide does not adhere to this format.

An actual entry could for example be

db_url = "postgres://bomnipotent_user:${BOMNIPOTENT_DB_PW}@bomnipotent_db:5432/bomnipotent_db"

Let’s break this down:

  • The driver is “postgres”, which is the only driver currently supported.
  • The username is “bomnipotent_user”, because this is the value that was provided to PostgreSQL during the setup.
  • The password is read from the external variable “BOMNIPOTENT_DB_PW”, which may be provided via environment or .env file. You could also store it in the config file directly, but this is considered bad practice.
  • The domain is “bomnipotent_db”, which is the name of the docker container running the database. For a database in the same environment this would instead be “localhost”, and for an external database it would be some other domain or IP address.
  • The port is 5432, which is the default port that the PostgreSQL listens to. In this case the docker container is in the same docker network as the BOMnipotent Server container. Without this direct connection, you would need to map this internal port to an arbitrary external port in the docker setup, and provide this external port in the config.
  • The database itself is also called “bomnipotent_db”, because this is the value that was provided to PostgreSQL during the setup.

If BOMnipotent Server cannot reach the database, it will let you know in the logs.

Mar 9, 2025

Server Domain

BOMnipotent Server is not only reachable per API, but also displays some static XML and HTML pages. One important example is that it may generate CSAF Provider Metadata for you. As some of these pages reference one another, the server needs to know the full domain behind which it is reachable from the internet.

The parameter is simply called “domain”. Providing a protocol is optional, “https” is assumed as a default.

The relevant part in the config file may for example look like this:

domain = "https://bomnipotent.wwh-soft.com"
domain = "bomnipotent.wwh-soft.com"

Mar 9, 2025

TLS Config

The TLS configuration does not support hot reloading. You will need to restart the server after modifying it.

What is TLS?

This section serves to give people who never had to deal with TLS a broad understanding of the process. Feel free to skip ahead to the section about configuration .

Transport Layer Security (TLS), sometimes also called by its legacy name Secure Socket Layer (SSL), is what puts the “S” in “HTTPS”. It is a very sophisticated, smart and widely used method for end-to-end encrypting of communication over the internet, but it can also lead to a lot of head scratching.

In very broad terms, TLS works as outlined in the following paragraphs:

Your server generates a pair of secret and public key. Anyone can use the public key to either encrypt a message that only the secret key can decrypt, or to decrypt a message that was encrypted with the secret key.

When a client reaches out to your sever asking for encryption, the latter responds by sending a TLS certificate. It contains several important field:

  • Your severs public key, which the client can now use to send messages only the server can read.
  • The domain(s) this certificate is valid for. These are usually stored in the field “Subject Alternative Names” (SAN), and are meant to ensure that the client is really talking to the address it wanted to reach.
  • A digital signature, cryptographically proving that the certificate was not altered along the way.
  • Many more. Just have a look at any certificate in your browser.

The digital signature is not created with the server’s secret key, because the client does not trust that key yet. Instead, there are some (several hundred) public keys stored on your machine that belong to so called “Root Certificate Authorities”. The job of these is to sign server certificates after they have verified that the bearer of the secret key is also the owner of the domains they claim.

For practical reasons the root CAs usually do not directly sign a server certificate, but rather an intermediate certificate, which is then used to sign the final certificate.

All in all, the chain of trust thus looks like this:

  1. The Client trusts the root CA.
  2. The root CA trusts the intermediate CA.
  3. The intermediate CA trusts the server.

Thus, the client decides to trust the server and establich an encrypted connection.

TLS Configuration

Because BOMnipotent is secure-by-default, it requires you to make at least one statement about TLS encryption. The “tls” section of your configuration file accepts the following fields:

[tls]
allow_http = false # Optional, is false by default
certificate_chain_path = "your-chain.crt"
secret_key_path = "your-key.pem"

Providing the TLS certificate paths is required if HTTP is not allowed (because the server could not offer any connection otherwise), and it is optional if HTTP is explicityl allowed by setting allow_http to true. If you set either the certificate_chain_path or the secret_key_path , you will also need to set the other. Furthermore, the server checks if there is a mismatch between the two.

allow_http

If you set this optional field to true, your BOMnipotent Server will allow unencrypted connections. This makes sense if your server is running behind a reverse proxy, and is communicating to it only over the local network. In this setup, the server is not directly reachable from the internet, so the reverse proxy can handle encryption for it.

The default port for HTTPS is 8080, but it can be freely configured .

Please note that the OASIS CSAF Standard requires that the access to your CSAF documents is encrypted!

secret_key_path

The value of “secret_key_path” needs to point to a file that is reachable for BOMnipotent server. One common value is:

secret_key_path = "/etc/ssl/private/<yourdomain>_private_key.key"

If your BOMnipotent Server is running inside a docker container, you may want to bind mount the container directory “/etc/ssl” to the directory with the same name on the host.

The file needs to be an ASCII-armoured secret key in PEM-Format . Luckily, this is the format you typically receive when obtaining a TLS certificate.

The contents of the file could for example look like this:

-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIHru40FLuqgasPSWDuZhc5b2EhCGGcVC+j3DuAqiw0/m
-----END PRIVATE KEY-----

This is the secret key from a certificate generated for the BOMnipotent integration tests.

certificate_chain_path

Likewise, the “certificate_chain_path” needs to point to a file reachable by BOMnipotent Server. A typical location is:

certificate_chain_path = "/etc/ssl/certs/<yourdomain>-fullchain.crt"

The chain needs to contain all certificates in the chain of trust concatenated together, beginning with the one for your sever and ending with the root certificate authority.

The contents of the full certificate chain for the integration test looks like this:

-----BEGIN CERTIFICATE-----
MIIB8jCCAaSgAwIBAgIBAjAFBgMrZXAwPTELMAkGA1UEBhMCREUxHDAaBgNVBAoT
E0JPTW5pcG90ZW50IFRlc3QgQ0ExEDAOBgNVBAMTB1Rlc3QgQ0EwHhcNMjUwMzA1
MTMxNzEwWhcNMjUwNDI0MTMxNzEwWjBFMQswCQYDVQQGEwJERTEgMB4GA1UEChMX
Qk9Nbmlwb3RlbnQgVGVzdCBTZXJ2ZXIxFDASBgNVBAMTC1Rlc3QgU2VydmVyMCow
BQYDK2VwAyEAMzw8ZVgiuP3bSwh+xcBXu62ORwakr/D+s0XQks1BTFOjgcAwgb0w
DAYDVR0TAQH/BAIwADBIBgNVHREEQTA/gglsb2NhbGhvc3SCCTEyNy4wLjAuMYIT
c3Vic2NyaXB0aW9uX3NlcnZlcoISYm9tbmlwb3RlbnRfc2VydmVyMBMGA1UdJQQM
MAoGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIGwDAdBgNVHQ4EFgQUC/RAGubXMfx1
omYTXChtqneWDLcwHwYDVR0jBBgwFoAUStstIFLzDjBSHYSsSr9hSgRVZT4wBQYD
K2VwA0EAXN/6PpJQ0guaJq67kdKvPhgjWVdfxxeCAap8i24R39s7XxNz5x5lPyxA
DQG63NS/OED43+GfpkUguoKxfZLBBA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBazCCAR2gAwIBAgIBATAFBgMrZXAwPTELMAkGA1UEBhMCREUxHDAaBgNVBAoT
E0JPTW5pcG90ZW50IFRlc3QgQ0ExEDAOBgNVBAMTB1Rlc3QgQ0EwHhcNMjUwMzA1
MTMxNzEwWhcNMjUwNjEzMTMxNzEwWjA9MQswCQYDVQQGEwJERTEcMBoGA1UEChMT
Qk9Nbmlwb3RlbnQgVGVzdCBDQTEQMA4GA1UEAxMHVGVzdCBDQTAqMAUGAytlcAMh
AIoFFlU/ADa77huqAb5aBY9stDwzzDd/Tdocb9RZDBG2o0IwQDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUStstIFLzDjBSHYSsSr9h
SgRVZT4wBQYDK2VwA0EARYL+v9oDGjaSW5MhjjpQUFXnAVMFayaKFfsfbbkmTkv4
+4SRWFb4F/UULlbbvlskSgt8jAaaTSk7tu/iX67YDw==
-----END CERTIFICATE-----

The first certificate (“MIIB8j…”) authenticates the server, the second one (“MIIBaz…”) is that of the root CA.

There is no intermediate certificate authority here because it is not required for the tests. In your productive environemnt with real certificates you will most likely need to add an intermediate certificate in the middle.

Apr 26, 2025

SMTP Settings

Simple Mail Transfer Protocol (SMTP) is the protocol used for sending emails. In the context of BOMnipotent, the server uses it to verify that newly requested users have access to the email address they provided. To enable the server to do that, it needs to be taught to reach an SMTP endpoint.

Configuring SMTP

The smtp section of the configuration file looks like this:

[smtp]
user = "you@yourdomain.com"
endpoint = "your.smtp.host"
secret = "${SMTP_SECRET}" # Optional
starttls = true # Optional, defaults to false

Inputs

User

The SMTP user is the email address you use when authenticating with your mail server.

Endpoint

The SMTP endpoint tells BOMnipotent where to send the data to. It can point directly to your mailserver, or to an SMTP relay.

If the endpoint begins with the “smtp://” marker, BOMnipotent Server will try to directly connect to that URL. This is typically what you want when connecting to a locally running relay. Otherwise, it will connect to an external relay.

Secret

When directly communicating with your mailserver, you have to authenticate. The secret will either be your password, or an API key if your mail provider offers one. In the example above, the secret is read from an environmen variable called “SMTP_SECRET”, which could for example come from a .env file.

A locally running SMTP relay does not necessarily require a password to accept mails, which is why this field is optional.

STARTTLS

STARTTLS is one option to encrypt sending emails, the other being SMTPS. Since 2018, the Internet Engineering Task Force recommends against using STARTTLS . However, if your mailserver does not support SMTPS, STARTTLS is better than no encryption, which is why it is still supported by BOMnipotent Server.

Skipping User Verification

If you do not (yet) have access to an SMTP server, you can eliminate the need for the smtp configuration by adding the following line to the global context (meaning at the beginning) of your config.toml:

skip_user_verification = true

BOMnipotent Server will then not send a verification email to newly requested users. It will instead log a warning message each time it does not send said mail, because this configuration reduces the security of your server.

May 17, 2025

CSAF Provider Metadata

The OASIS Standard requires that providers of CSAF documents offer a “provider-metadata.json”, which needs to follow a specific schema .

BOMnipotent strives to make it as easy as possible to fulfil this requirement. You can either generate the file from a few inputs, or provide a file for BOMnipotent to load.

Generating Data

By providing some user-specific inputs, you can make BOMnipotent generate a valid provider-metadata.json file. This is much easier than creating the file by hand, but offers somewhat less control.

The relevant section in your config file looks like this:

[provider_metadata.publisher]
name = "<name of your organsiation>"
namespace = "https://<your-domain>.<top-level>"
category = "vendor"
issuing_authority = "<Additional info>" # Optional
contact_details = "<Please contact us at...>" # Optional

Publisher Data Inputs

Name

This field requires you to provide the name of your company, organisation, or you as an individual.

Namespace

While the “name” field is primarily aimed at humans, the “namespace” is used by machines to identify your organisation across various security documents. It needs to be in URI format, including protocol, domain and top-level domain. Because it refers to your whole organisation, it should not contain a subdomain.

This is what sets the “provider_metadata.publisher.namespace” field apart from the “domain” configuration: The latter points to your instance of BOMnipotent Server, while the former is much more general.

Category

The publisher category is a machine readable classification of who you are. According to the CSAF Standard , the publisher categories allows the values “coordinator”, “discoverer”, “other”, “translator”, “user” or “vendor”. As a user of BOMnipotent Server, you are most likely a “vendor”, meaning a developer or retailer of products or a service.

Issuing Authority

This optional field can be used to clarify your connection to the hosted documents. Are you the developer and maintainer? Are you a retailer? The input is in free form.

Contact Details

This optional field allows you to offer contact details for general or security inquiries. The input is a string in free form.

Generated Document

Once you have provided the data and started the server, the generated document will host it under “your-domain/.well-known/csaf/provider-metadata.json”. You can see a live example here and a static example here:

{
    "canonical_url": "https://bomnipotent.wwh-soft.com/.well-known/csaf/provider-metadata.json",
    "distributions": [
        {
            "rolie": {
                "feeds": [
                    {
                        "summary": "WHITE advisories",
                        "tlp_label": "WHITE",
                        "url": "https://bomnipotent.wwh-soft.com/.well-known/csaf/white/csaf-feed-tlp-white.json"
                    },
                    {
                        "summary": "GREEN advisories",
                        "tlp_label": "GREEN",
                        "url": "https://bomnipotent.wwh-soft.com/.well-known/csaf/green/csaf-feed-tlp-green.json"
                    },
                    {
                        "summary": "AMBER advisories",
                        "tlp_label": "AMBER",
                        "url": "https://bomnipotent.wwh-soft.com/.well-known/csaf/amber/csaf-feed-tlp-amber.json"
                    },
                    {
                        "summary": "RED advisories",
                        "tlp_label": "RED",
                        "url": "https://bomnipotent.wwh-soft.com/.well-known/csaf/red/csaf-feed-tlp-red.json"
                    },
                    {
                        "summary": "UNLABELED advisories",
                        "tlp_label": "UNLABELED",
                        "url": "https://bomnipotent.wwh-soft.com/.well-known/csaf/unlabeled/csaf-feed-tlp-unlabeled.json"
                    }
                ]
            }
        }
    ],
    "last_updated": "2025-03-06T16:13:24.632235974Z",
    "list_on_CSAF_aggregators": true,
    "metadata_version": "2.0",
    "mirror_on_CSAF_aggregators": true,
    "publisher": {
        "category": "vendor",
        "contact_details": "For security inquiries, please contact info@wwh-soft.com",
        "name": "Weichwerke Heidrich Software",
        "namespace": "https://wwh-soft.com"
    },
    "role": "csaf_provider"
}

This file contains a ROLIE feed for all your CSAF documents, which is probably the main reason why you do not want to create this document by hand.

The “last_updated” field is generated from the last modification timestamp of your config file.

BOMnipotent assumes that you want your CSAF documents listed and mirrored on publicly available repositories. This only concerns the documents labeled TLP:WHITE / TLP:CLEAR! The aggregators do not have access to any documents classified otherwisely.

BOMnipotent helps you fulfil all requirements to be a CSAF Provider , according to the OASIS standard.

Providing Filepath

If for some reason you want to have full control of the provider-metadata document, you can provide a filepath in the config file:

[provider_metadata]
path = "<filepath>"

BOMnipotent will then read the file, check that it follows the provider-metadata json schema , and host it.

You have to provide either a path to a file, or publisher data. If you provide neither or both, the config will not be loaded.

May 17, 2025

Optional Configs

This section lists all optional configuration parameters for BOMnipotent. They can be used to further customise the server’s behaviour. If they are not provided, they assume a meaningful default value.

BOMnipotent is secure-by-default, meaning you can in good conscience ignore these parameters for the inital sever setup.

Apr 26, 2025

Subsections of Optional Configs

Logging

To keep track of the history of interactions with BOMnipotent Server, every event potentially results in a log being written. These logs are printed to the standard output by default. If the server is running inside a docker container named “bomnipotent_server”, you can see the last 3 lines of logs by calling

docker logs bomnipotent_server -n 3
2025-03-08 09:16:10 +00:00 [INFO] Database is ready.
2025-03-08 09:16:15 +00:00 [DEBUG] Header X-Auth-Email was not found in request
2025-03-08 09:16:15 +00:00 [DEBUG] Received healthcheck request from 127.0.0.1

The logs have the format “Date, Time, Timezone, Log Level, Message”.

The relevant section of your config file to manipulate logging looks like this:

[log] # This section is optional
level = "debug" # Default severity level is "info"
file = "/var/log/bomnipotent.log" # Default is to log to stdout

The logs do not contain confidential information. For example, the notification about the database connection obfuscates username and password:

2025-03-08 09:16:10 +00:00 [INFO] Creating connection pool for database: postgres://[user]:[password]@database:5432/bomnipotent_db

Severity Level

BOMnipotent Server supports five log levels:

Each severity level includes the log messages from the previous ones. The default severity level is “info”, meaning it prints log messages with severities “error”, “warn” and “info”.

Error

An error indicates that an operation has to be aborted. Either the user input or the configuration has to change for it to work, and it needs to be triggered again.

Common examples include:

  • A requested resource is not found.
  • The user does not have sufficient permissions.
  • The configuration file cannot be loaded.

Warn

A warning is displayed when some input or configuration is suboptimal. The operation is still completed, but you or the user are urged to change either input or configuration.

Common examples include:

  • A temporary admin is configured.
  • A document without TLP classification is requested, but no default-tlp has been configured.
  • You have not updated BOMnipotent for over a year.

Info

This is the default severity level for logs.

Logs with level info indicate regular events that are important, but rare enough to not overwhelm the output.

Common examples include:

  • A request was made to an endpoint (although some endpoints like /health log with a lower severity than info).
  • A user was authenticated.
  • The config file has successfully been reloaded.

Debug

Configure debug log severity level to find errors in the configuration or user input. These logs help to understand, step by step, what the program does, and where something may go wrong.

Common exmples include:

  • Responses sent to the client.
  • Interactions with the database.
  • The contents of a succesfully reloaded config file.

Trace

As the lowest possible severity level, trace logs contain a lot of output. In contrast to the debug level, the trace level is meant to assist in finding errors in BOMnipotent itself. It is thus unlikely that you will ever need to configure this level, because it is mainly targeted at the developer. Common examples include:

  • The body of a request (truncated after 1000 characters).
  • The filters applied to a database request.
  • The server received a file event because someone interacted with the config file (or any adjacent file).

Logfile

If you provide a valid path for the “file” key under the “log” section of your config file, BOMnipotent Server will print its outputs there instead of stdout. This is mainly useful if you run the server outside of any container.

[log]
file = "/var/log/bomnipotent.log"

If the file already exists, BOMnipotent Server will mercilessly overwrite it upon a restart, if it has the permissions to do so.

Apr 26, 2025

Temporary Admin

Only an admin can give admin permissions to a user. In order to create the first admin, you therefore need to enable these permissions via another, temporary path. This is done with the config parameter “tmp_admin”, which takes the email of a user as an input:

tmp_admin = "<email>"

The whole procedure is described in the setup instructions .

For security reasons, the rules surrounding temporary adminship are rather strict, and allow only one specific order of operations:

  1. Request a new user. The “tmp_admin” parameter may not be set at this point, or the request is denied by the server. A request for a new user with the same email is also denied.
  2. Mark that user as a temporary admin in the configuration. If the user does not exists, the configuration will fail to load.
  3. Make the user a proper admin by approving them and adding the admin role.
  4. Remove the “tmp_admin” parameter from the server configuration. The server logs will print warning messages while it is still active.

If the rules were less strict and you could designate a temporary admin before the user is registered in the database, an attacker could potentially request a new user with the correct email address and would then have admin permissions on your system.

Mar 9, 2025

TLP Config

CSAF documents can and in fact should be classified using the Traffic Light Protocol (TLP) . It clarifies if and with whom you can share documents that you have access to. The somewhat older TLP v1.0 standard knows four different classifications:

  • TLP:RED: This document may not be disclosed to anyone else.
  • TLP:AMBER: This document can be spread on a need-to-know basis within their organization and its clients.
  • TLP:GREEN: This document can spread this within the recipient’s community.
  • TLP:WHITE: There is no limit on disclosure.

The more current TLP v2.0 standard replaces TLP:WHITE with TLP:CLEAR, and adds the new classification TLP:AMBER+STRICT, which only allows sharing on a need-to-know basis with the recipient’s organisation, but not beyond.

Documents hosted by BOMnipotent Server that are classified as TLP:WHITE or TLP:CLEAR are visible to everyone, be they admin, completely unauthenticated user or crawler bot!

The “tlp” section of your configuration file may contain the following fields:

[tlp]
allow_tlp2 = true # Default is false
default_tlp = "amber+strict" # Default is "red"

Allowing TLP v2.0

The current OASIS CSAF Standard requires documents to be classified with TLP v1.0 labels. However, many companies would prefer to use the TLP:AMBER+STRICT classification from the TLP v2.0 standard for their documents. Furthermore, the TLP v2.0 standard will become mandatory once the CSAF standard 2.1 is released.

To be fully compliant with the CSAF standard, BOMnipotent does not allow TLP v2.0 labels by default. You can, however, set the field “allow_tlp2” to true in the “tlp” section of your config file:

[tlp]
allow_tlp2 = true

If you do, both TLP v1.0 and TLP v2.0 labels will be accepted.

If you do not, and BOMnipotent encounters TLP v2.0 labels, it will silently convert TLP:CLEAR to TLP:WHITE. Because TLP:AMBER+STRICT has no direct equivalent in TLP v1.0, BOMnipotent will take the conservative approach, convert it to TLP:RED, and log a warning.

Default TLP

Classifying a CSAF document with a TLP label is optional, and a TLP classification is not even part of the CycloneDX standard for BOM documents. BOMnipotent needs to at least know if the document is labelled TLP:CLEAR / TLP:WHITE and thus publicly available, or if access to it is restricted.

It is good practice to define a TLP classification that BOMnipotent can fall back to for an unlabelled document. You can do that in your config file via:

[tlp]
default_tlp = "amber"

The deserialisation gives you some leeway: It does not consider the casing, and the “TLP:” prefix is optional. The values “amber”, “AMBER”, “tlp:amber” and “TLP:AMBER” are all recognised as TLP:AMBER.

If you do not provide a default TLP label, and BOMnipotent encounters an unlabelled document, it will default to TLP:RED and log a warning.

The default TLP label is evaluated at the time of access, not at the time of writing. Unlabelled documents remain unlabelled in the database. If at any point you change the default TLP label, you thus change it for all unlabelled documents of past and future .

Mar 9, 2025

User Expiration

When a request for a new user account is made, it deposits a public key in the database.

This key has an expiration data, after which it is not accepted anymore. This can be seen by calling:

./bomnipotent_client user list
╭───────────────────┬──────────┬───────────────────────────┬───────────────────────────╮
│ User Email        │ Status   │ Expires                   │ Last Updated              │
├───────────────────┼──────────┼───────────────────────────┼───────────────────────────┤
│ info@wwh-soft.com │ APPROVED │ 2026-03-03 11:30:15.62432 │ 2025-03-02 11:47:38.51048 │
│                   │          │ 5 UTC                     │ 5 UTC                     │
╰───────────────────┴──────────┴───────────────────────────┴───────────────────────────╯

By default, the key is valid for a little over a year after it was requested. This time can be configured with the “user_expiration_period” parameter. As values it accepts a time period in the human readable format “ ”, where “” can be any of “year”, “month”, “week”, “day”, or their plural variants.

user_expiration_period = "366 days"
user_expiration_period = "5 years"
user_expiration_period = "1 month"
user_expiration_period = "3 weeks"
user_expiration_period = "5 days"

If you really hate your users you could also use “hours”, “minutes”, seconds", “ms”, “us” or “ns” as units. BOMnipotent does not question how realistic your expectations are.

Changing this configuration does not affect the expiration dates of existing users! It will only influence how much time is given to newly requested users.

Mar 16, 2025

DOS Prevention

Denial of Service (DOS) attacks target programs or servers with the goal of making them unresponsive. They are notoriously hard to mitigate, because they are often based on flooding the service with otherwise legitimate requests.

BOMnipotent has several DOS prevention mechanisms in place (from disallowing any code that might crash the server to limiting the length of log outputs), but one particular technique can be tweaked by the user.

If the number of requests from a single source exceeds a limit within a time period, that source is put on a greylist for one hour, which automatically denies requests.

By default, this happens if the number of requests exceeds 100 within one minute, but you can modify this behaviour in your config file:

[dos_prevention]
limit = 50 # Default is 100
period = "30 seconds" # Default is "1 minute"

This new config would make the server react faster to a possible DOS attack, but has a higher risk to falsely classify request as an attack.

Mar 9, 2025

Port Binding

While IP addresses are used to identify your server on the internet, ports are used to identify individual services running on it. Upon startup, BOMnipotent offers one or two services: An HTTP service for unencrypted communication, an HTTPS service for encrypted communication, or both.

Important: Use unencrypted communication only within a machine! Pure HTTP communication over the internet is a thing of the past, and rightly so.

By default, BOMnipotent offers its services over the ports 8080 and 8443. It does so to avoid collisions, and because it is primarily intended to be run inside a container. If you want to run BOMnipotent without the container abstraction, you can instead offer the services via the ports typically associated with HTTP and HTTPS:

http_port = 80 # Default is 8080
https_port = 443 # Default is 8443

Any other unsigned 16 bit number will also do.