Carbone On-Premise

Generate and manage your reports from your own servers.

It provides the same API as the Carbone Cloud API, except that you are unlimited in usage, details:

  • Unlimited document generation and formats
  • Unlimited usage in time
  • Unlimited servers & installations
  • All enterprise features (dynamic images, colors, html rendering, ...)
  • A stateless an light version of Carbone Studio with basic authentication
  • A binary file easy to install. It can be used in containers, or installed with a self-configured and highly secured systemd's sandbox of Ubuntu & Debian
  • Customise your storage, and authentication thanks to NodeJS plugins.
  • SLA support
  • All incoming new features, updates and fixes.

The license price of Carbone On-premise depends on your company revenues and all subsidiaries. The license is valid only for one project or one product.

To get more information about the pricing, or get a trial license, reach us on the chat or by email: contact@carbone.io.

Client Side

API flow

Carbone On-Premise works the same way as Carbone Cloud API,

To generate a document, you have to:

  1. Upload a template POST /template to get a template ID. The template is stored in your file system into the directory template. It is possible to store templates into a custom storage system (s3, object storage)
  2. Generate a report POST /render/:templateId from the template ID and a JSON data-set, then the request returns a unique usable render ID.
  3. Download the report GET /render/:renderId thanks to the render ID.
  4. To get a new document, repeat the process from point 2.

API endpoints

API integration

Software Development Kits

SDKs are available to integrate the Carbone Cloud API into your service/application in a few lines of code:

API Clients for Windows / MacOS / Linux

To test the Cloud API in a few minutes, load a pre-made API specification into:

Terminal

Create your own API integration

Learn more on the Cloud API specification, all API endpoint are details to create your HTTP client on your own language.

Server Install

Carbone On-premise can be installed in different ways:

  • Self-contained binary executable + external LibreOffice
  • Docker container (Libre Office is included)

Basic Install

  1. Download the license and the Carbone On-premise binary for server/OS: Mac, Linux or Windows
  2. On your terminal run chmod +x carbone-ee in order to execute the binary.
  3. Install LibreOffice Link to instructions.
  4. Prepare the license key to be loaded, multiple solution:
    • Set the Environment Variable CARBONE_EE_LICENSE with the license key as the value
    • Or pass the CLI option --license followed by the license key as the value when you start the server (step 5)
    • Or insert the license file in the "config" directory. If the directory doesn't exist, it must be created next to the Carbone On-premise binary. If multiple licenses are available, only the latest license is selected. The binary can't start if the license is outdated or invalid.
  5. Start Carbone web server. It is possible to pass options to Carbone On-Premise through the CLI:
  ./carbone webserver --port 4000 --workdir .

If an error appears during the start up, you must verify:

  • if your license is valid
  • if CLI options and values are valid

Systemd Install

Carbone On-Premise contains automatic installation scripts to daemonize with systemd. It has been carefully configured to provide a high level of security. The Systemd installation is working only for Ubuntu or Debian.

  # Generate installation scripts
  ./carbone install
  # Execute installation scripts and follow instructions
  sudo ./install.sh

The service is configured to run with "carbone" user (automatically created) in the "/var/www/carbone-ee" directory. It is possible to overwrite values through environment variables CARBONE_USER and CARBONE_WORKDIR.

You must install LibreOffice to generate PDF documents, read instructions.

Docker CLI Install

Installing Carbone On-premise with docker doesn't need LibreOffice to be installed, it is already included on the Docker image.

Build the image with the command:

$ docker build --platform "linux/amd64" -t carbone-ee:latest .

Then start the container:

$ docker container run --name carbone-ee -p 4000:4000 --platform linux/amd64 --volume=/var/tmp/key:/key --env CARBONE_EE_LICENSEDIR=/key --entrypoint ./carbone-ee-linux carbone-ee:latest webserver
## For background mode, add the -d option

The server is listening by default on http://localhost:4000. To change configuration, add CLI options at the end of the command.

Stop the container:

$ docker stop carbone-ee

Remove the container:

$ docker container rm carbone-ee

Docker Compose Install

Installing Carbone On-premise with docker doesn't need LibreOffice to be installed, it is already included on the Docker image.

Open a terminal where the Dockerfile and the docker-compose.yml are located.

Start the server with the command:

$ docker-compose up
## For background mode, add the -d option

The server is listening by default on http://localhost:4000. To change configuration, add CLI options on the docker-compose.xml.

Command to stop the container without removing it:

$ docker-compose stop

Command to stop the container and removes the container, networks, volumes, and images created by up:

$ docker-compose down

Discover more commands by reading the docker-compose CLI overview.

LibreOffice Install

Carbone uses efficiently LibreOffice to convert documents. Among all tested solutions, it is the most reliable and stable one in production for now. Behind the scene, Carbone server does:

  • starts LibreOffice in "server-mode": headless, no User Interface loaded
  • manages multiple LibreOffice workers to maximize performance (configurable number of workers)
  • automatically restarts LibreOffice worker if it crashes or does not respond
  • job queue, re-try conversion two times if something bad happen

Install for:

  # remove all old version of LibreOffice
  sudo apt remove --purge libreoffice*
  sudo apt autoremove --purge

  # Download LibreOffice debian package. Select the right one (64-bit or 32-bit) for your OS.
  # Get the latest from http://download.documentfoundation.org/libreoffice/stable
  # or download the version currently "carbone-tested":
  wget https://downloadarchive.documentfoundation.org/libreoffice/old/7.4.1.1/deb/x86_64/LibreOffice_7.4.1.1_Linux_x86-64_deb.tar.gz

  # Install required dependencies on ubuntu server for LibreOffice 7.0+
  sudo apt install libxinerama1 libfontconfig1 libdbus-glib-1-2 libcairo2 libcups2 libglu1-mesa libsm6

  # Uncompress package
  tar -zxvf LibreOffice_7.4.1.1_Linux_x86-64_deb.tar.gz
  cd LibreOffice_7.4.1.1_Linux_x86-64_deb/DEBS

  # Install LibreOffice
  sudo dpkg -i *.deb

  # If you want to use Microsoft fonts in reports, you must install the fonts
  # Andale Mono, Arial Black, Arial, Comic Sans MS, Courier New, Georgia, Impact,
  # Times New Roman, Trebuchet, Verdana,Webdings)
  sudo apt install ttf-mscorefonts-installer

  # If you want to use special characters, such as chinese ideograms, you must install a font that support them
  # For example:
  sudo apt install fonts-wqy-zenhei

Server Options

Options list

Carbone default parameters can be overwritten through:

If an option is reported in different places, CLI options are picked in priority, then environment variables in second place, and finally the configuration file.

Options Default Values Description CLI options ENV
port 4000 Service PORT --port / -p CARBONE_EE_PORT
workdir Actual directory Define the place to store elements, it creates 6 directories:
- template : where carbone keeps templates (cache)
- render : temp directory where report are generated,
- asset : internal used only,
- config : config, licenses and ES512 keys for authentication,
- logs : [NOT IMPLEMENTED YET] formatted output logs, and
- plugin : where to put custom plugin
--workdir / -w CARBONE_EE_WORKDIR
licenseDir "config/" Absolute directory path to licenses --licenseDir / -L CARBONE_EE_LICENSEDIR
license License as a string, if the option is used, licenseDir option is skipped --license / -l CARBONE_EE_LICENSE
factories 1 Multithread parameter, number of LibreOffice converter --factories / -f CARBONE_EE_FACTORIES
attempts 1 If LibreOffice fails to convert one document, attempts options set the number of re-try --attemps / -a CARBONE_EE_ATTEMPTS
authentication false Authentification documentation at the following link --authentication / -A CARBONE_EE_AUTHENTICATION
studio false Web interface to preview reports. Learn more. --studio / -s CARBONE_EE_STUDIO
studioUser admin:pass If the authentication option is enabled, the browser requests an authentication to access the web page. Credentials have to be formated, such as: [username]:[password]. --studioUser / -S CARBONE_EE_STUDIOUSER
maxDataSize 60MB Maximum JSON data size accepted when rendering a report, the value must be bytes. Calcul example: 100 * 1024 * 1024 = 100MB --maxDataSize / -mds CARBONE_EE_MAXDATASIZE
templatePathRetention 0 Template path retention in days. 0 means infinite retention. --templatePathRetention / -r CARBONE_EE_TEMPLATEPATHRETENTION
lang en Locale language used by Carbone --lang / -l CARBONE_EE_EN
timezone Europe/Paris Timezone for managing dates --timezone / -t CARBONE_EE_TIMEZONE
currencySource Currency source for money conversion. If empty, it depends on the locale. --currencySource / -cs CARBONE_EE_CURRENCYSOURCE
currencyTarget Currency target for money conversion. If empty, it depends on the locale. --currencyTarget / -ct CARBONE_EE_CURRENCYTARGET
currencyRates { EUR : 1, USD : 1.14, ... } Currency rates, it is based on EUR which should be equals to "1". The option can only be set on the config/config.json file.
translations {} Translation object loaded at startup. It can be overwritten by rendering requests. The option can only be set on the config/config.json file.
converterFactoryTimeout 60000 Maximum conversion/socket timeout for one render (unit: ms) CARBONE_EE_CONVERTERFACTORYTIMEOUT
xlsmEnabled false Accept xlsm export --xlsmEnabled / -xe CARBONE_EE_XLSMENABLED

Options through CLI

To list available options, run the help command, such as:

./carbone webserver --help

Here is an example of passing options to the service:

./carbone webserver --port 4001 --factories 4 --workdir /var/www/carbone --attemps 2 --authentication --studio

Options as Configuration file

To use a configuration file, config.json must be created in the config folder. Here is an example of a configuration:

{
  "port": 4001,
  "bind": "127.0.0.1",
  "factories": 4,
  "attempts": 2,
  "authentication": true,
  "studio" : true,
  "studioUser" : "admin:pass" // login:password if authentication is active
}

Options as Environment variables

Environment variables can be used to define options, the name has to be uppercased and has to start with the prefix "CARBONE_EE_", such as:

export CARBONE_EE_PORT=3600
export CARBONE_EE_BIND=127.0.0.1
export CARBONE_EE_FACTORIES=4
export CARBONE_EE_WORKDIR=/var/www/carbone
export CARBONE_EE_ATTEMPTS=2
export CARBONE_EE_AUTHENTICATION=true

Server Authentication

About authentication

By default, all API endpoints are reachable without authentication, and all requests are authorized. An authentication system is available to restrict the access. When activated, all API endpoints are protected by authentication except GET /render/:renderId and GET /status.

Reasons why GET /render/:renderId is not protected:

  • :renderId contains the custom report name in base64, concatenated with a 22-characters-long cryptographically random numbers with unbiased tranformation. It is somehow a unique password for each rendered report. It is better than any authentication mechanism.
  • :renderId is ephemeral, the resource becomes inaccessible once the file is downloaded.
  • The report can be easily downloaded in any browsers / reverse-proxy / application without complex authentication mechanisms

Enable authentication

Three solutions to enable authentication:

  • Passing the argument on the server CLI: --authentication
  • Or, setting on the configuration file: "authentication" : true
  • Or, setting the environment variable: CARBONE_EE_AUTHENTICATION=true

Then the header Authorization must be passed to all requests, with a ES512 JWT token as value. To generate a token, you can:

  • Use the CLI: ./carbone generate-token generates 40-years valid tokens
  • Using standard libraries in any programming languages https://jwt.io/. Programmatically you can generate and renew tokens. The payload must be signed with the generated public key in config/key.pub (ES512). Tokens must contain at least the following payload:
    {
    "iss": "your-carbone-user",
    "aud": "carbone-ee",
    "exp": 2864189447
    }

Server Studio Light

Carbone Studio Light is a web interface to preview reports with a JSON editor. It is a "light" version of https://studio.carbone.io without files and version management. To enable the Carbone Studio Light:

  • Pass the CLI option --studio to the binary, such as: ./carbone webserver --studio
  • Or, set the env variable CARBONE_EE_STUDIO to true
  • Or, set on the configuration file "studio": true.

Then visit the URL http://127.0.0.1:4000 and enjoy!

If the authentication option is enabled, the browser requests an authentication to access the web page.

Server Customisation

Carbone On-premise gives the possibility to override functions at specific moments to add custom features, such as adding analytics, store data on a specific storage provider, custom authentication, etc... It is possible through:

  • Plugins: Functions to manage file storage, authentication, analytics, logs, or custom behaviors.
  • Middlewares: Functions that are fired before or after HTTP requests. It can be useful for logging, statistics, or custom behaviors.

requirement plugins and middlewares can be only written using NodeJS.

For a better understanding, here is the Carbone On-premise lifecycle:

image

Plugins

Following actions can be customized:

It is recommended to create a separate NodeJS repository:

# Create a separate folder
mkdir carbone-on-premise

# Initialize a node repository
npm init

# Move the carbone binary inside this folder
mv /path/to/carbone/binary ./carbone-on-premise

When Carbone On-premise is executed for the first time, default folders are created automatically (explanation here). Custom plugins MUST be inserted in the plugin folder.

Custom write template

To override template storage, create the file storage.js in the plugin folder. It will be possible to upload your template into an Object Storage, S3 API or other storage systems. Export a function called writeTemplate with 5 arguments:

  • req: The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on.
  • res: The res object represents the HTTP response that a server sends when it gets an HTTP request
  • templateId: Unique template ID as a sha256 hash
  • templatePathTemp: Absolute path of the uploaded template
  • callback: callback function

Additional file informations are available the request header:

  • req.headers['carbone-template-extension']: file extension (eg: 'xml')
  • req.headers['carbone-template-mimetype']: file mimetype (eg: 'application/xml')
  • req.headers['carbone-template-size']: file size in bytes
const fs = require('fs');
const path = require('path');
const os = require('os');

function writeTemplate (req, res, templateId, templatePathTemp, callback) {
  fs.rename(templatePathTemp, path.join(os.tmpdir(), 'PREFIX_' + templateId), (err) => {
    if (err) {
      return callback(err);
    }
    return callback(null);
  });
}

// Export the writeTemplate function
module.exports = {
  writeTemplate
}

Custom read template

To override template reading, the file storage.js in the plugin folder has to be created. The function to explort is readTemplate which is described as follow:

function readTemplate (req, templateId, callback) {
  // Read your template and return a local path for carbone
}

module.exports = {
  readTemplate
}

The function must return a local path because Carbone needs to read the file from a disk.

Custom delete template

To override template deletion, add the function deleteTemplate in the storage.js file in the plugin folder and export it, for instance:

// You can access req and res.
// For example, if you store your template on amazon S3, you could delete it
function deleteTemplate (req, res, templateId, callback) {
  // Delete the template and either return a local path to unlink it or just use res to return a response
  return callback(null, path.join(os.tmpdir(), 'PREFIX_' + templateId));
}

module.exports = {
  deleteTemplate
}

Custom write render

To override render writing, add the function afterRender in the storage.js file in the plugin folder and export it.

function afterRender (req, res, err, reportPath, reportName, statistics, next) {
  // Write or rename your render
  // TemplateID available: req.params.templateId
  return next(null)
}

module.exports = {
  afterRender
}

For example, this function can be used to move the render on Buckets, or it is also possible to change the response object:

res.send({
  success: true,
  data: {
    newField: reportName
  }
});

The statistics argument return an object with:

{
  "renderId": "",
  "template": "filename",
  "user"      : "if authentication enabled, the user ID is returned, it is coming from the JWT token",
  "options"   : "Rendering options as an object",
  "jsonSize"  : "size of the JSON data-set as bytes"
}

Custom read render

The function readRender has to be exported in the storage.js located in the plugin folder.

function readRender (req, res, renderName, next) {
  // Return the directly render or a local path
}

This function can be used to return directly the file using res , it is also possible to return the new render name, or return a path from the afterRender function.

// Return the new render name
function readRender (req, res, renderName, next) {
  return next(null, 'newRenderName')
}

// OR

// Return the new path on disk
function (req, res, renderName, next) {
  return next(null, renderName, '/new/path/on/disk')
}

Before/After Render

Before or after render plugin can be used to add pre or post rendering behavior, such as adding a prefix to the rendered filename. To do this, add a function beforeRender and/or afterRender in the storage.js plugin and export it. In these functions, the req request variable is available and options can be altered.

function beforeRender (req, res, carboneData, carboneOptions, next) {
  // add a prefix to rendered filename
  carboneOptions.renderPrefix = 'myPrefix';
  next(null);
}

Authentication (Get Public Key)

For the authentication checking, it is possible to define a new location to store public keys. The file authentication.js has to be created in the plugin folder. Export the function getPublicKey described as follow:

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

/**
 * @param {Object} req : Request from the request
 * @param {Object} res : Response from the request
 * @param {Object} payload : https://github.com/Ideolys/kitten-jwt#api-usage to see object details
 * @param {Function} callback : Function to call at the end
 * @return (publicKeyContent)
 */
function getPublicKey (req, res, payload, callback) {
  // Read the public key on disk or somewhere else
  fs.readFile(path.join(__dirname, '..', 'config', 'key.pub'), 'utf8', (err, content) => {
    if (err) {
      return callback(new Error('Cannot read public key ' + err.toString()));
    }

    // Return the public key content in the callback
    return callback(content)
  });
}

// Export the getPublicKey function
module.exports = {
  getPublicKey
}

Middlewares

Middlewares can be added before or after route. It can be useful to log or get stats about requests. Add a middlewares.js file in the plugin folder and export two arrays:

  • before
  • after
function beforeMiddleware (req, res, next) {
  console.log('I am executed before all route')
  return next()
}

function afterMiddleware (req, res, next) {
  console.log('I am executed after all route')
}

module.exports = {
  before: [beforeMiddleware], // Middlewares in this array will be executed before routes
  after: [afterMiddleware] // Middlewares in this array will be executed after routes
}