Emerald

Open source general purpose event logger

Emerald listens for event interactions and logs them to a database.

Emerald is a privacy focused event logger that does not involve external service providers, and does not store personal identifiable information.

Distribution

Emerald is distributed as open source and without license restriction. Information about the license is referenced in the included document: license.txt. Information about development is referenced in the included documents: notice.txt, version.txt.

Emerald 0.2.1 is the latest version and is distributed as open source code.

Compilation

Emerald is written in the Go programming language and depends on the standard library and PostgreSQL driver to compile. Go is compatible with common server operating systems and processor architectures. Emerald is developed and supported on select systems: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64.

Emerald compiles with different options: test, run, build, clean, install. The compile script is optional and automates the compile process.

% ./compile.sh

Operation

Emerald is operated through a command line interface. The default path is /usr/local/bin/emerald. The default paths can be changed with command options.

For security purposes, the process should be restricted to a dedicated emerald user and emerald group on the system.

Help

Print the help document.

% emerald -help

Version

Print the version number.

% emerald -version

Verbose

Set the verbose process mode. The verbose mode should be false for security purposes. Set the verbose mode to true to print all process information.

The default mode is false.

% emerald -verbose=boolean

Input Log

Set the input log file path. The process will log standard input to the input log file. The process will generate the input log file when missing.

The default path is /usr/local/var/log/emerald/input.log.

% emerald -log-input=string

Output Log

Set the output log file path. The process will log standard output to the output log file. The process will generate the output log file when missing.

The default path is /usr/local/var/log/emerald/output.log.

% emerald -log-output=string

Error Log

Set the error log file path. The process will log standard error to the error log file. The process will generate the error log file when missing.

The default path is /usr/local/var/log/emerald/error.log.

% emerald -log-error=string

Configuration

Set the configuration file path. The process will generate the configuration file when missing.

The default path is /usr/local/etc/emerald.cfg.

% emerald -cfg=string

Configuration

Emerald is intended to operate behind a reverse proxy. For security purposes, the configuration file should be restricted to a dedicated emerald user and emerald group on the system.

Global Configuration

The global configuration section defines: server.

Define multiple servers for concurrent server processes.

{
  "server": [
    { ... },
    { ... }
  ]
}

Server Configuration

The server configuration section defines: hostname, port, database hostname, database port, database name, database username, database password.

{
  "hostname": "localhost",
  "port": "0000",
  "database": {
    "hostname": "localhost",
    "port": "0000",
    "name": "example",
    "authentication": {
      "username": "example",
      "password": "example"
    }
  }
}

Events

Emerald is controlled through an application programming interface. These examples are provided in the Javascript programming language.

Request Log

The event is sent and logged to the database.

curl https://localhost/emerald \
--request POST \
--header "Accept: application/json; charset=utf-8" \
--header "Content-Type: application/json; charset=utf-8" \
--data '{"example": "example"}' \
function requestLog(data) {
  fetch("https://localhost/emerald", {
    method: "POST",
    headers: {
      "Accept": "application/json; charset=utf-8",
      "Content-Type": "application/json; charset=utf-8"
    },
    body: JSON.stringify(data)
  })
}

Generate Log

The event registers and generates different event modes.

function generateLog(event, mode) {
  switch (mode) {
    case "example":
      requestLog({
        query: {
          event:    `${event.target}`,
          location: `${window.location.href}`,
          referrer: `${document.referrer}`
        },
        identifier: {
          session:    `${window.sessionStorage.getItem("uid")}`,
          persistent: `${window.localStorage.getItem("uid")}`
        },
        device: {
          language: `${window.navigator.language}`,
          agent:    `${window.navigator.userAgent}`
        }
      });
      break;
  }
}

Store Identifier

The event stores an identifier using web storage.

function storeIdentifier() {
  if (window.sessionStorage.getItem("uid") == null) {
    window.sessionStorage.setItem("uid", generateIdentifier());
  }
  if (window.localStorage.getItem("uid") == null) {
    window.localStorage.setItem("uid", generateIdentifier());
  }
}

Generate Identifier

The event generates a cryptographically secure identifier.

function generateIdentifier() {
  const value = new Uint32Array(32);
  const source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  let identifier = "";
  
  window.crypto.getRandomValues(value);
  
  for (let index = 0; index < 32; index++) {
    identifier += source[value[index] % source.length];
  }
  
  return identifier;
}