-
Notifications
You must be signed in to change notification settings - Fork 0
Backend specification
Octosign can use different signing backends that are dynamically loaded. The backend can be written in any language and with any directory structure and architecture. There are only two requirements:
-
Config file
backend.yml
in the root directory with information about the backend. - Use of plaintext communication protocol using the standard streams - standard input (STDIN), standard output (STDOUT), and standard error (STDERR).
Each backend provides a necessary amount of information for the application, like the name, description, or the executable that should be called. Here is an example of backend.yml
:
name: Electronic signature
description: Advanced electronic signature usable on PDF and other documents.
repository: https://github.com/durasj/octosign-dss
version: 1.0-SNAPSHOT
author: Jakub Ďuraš <[email protected]>
license: MIT
exec: ./jdk/bin/java.exe -jar ./sign.jar
build: bash -e ./dist.sh
The config file can define the following properties.
Property | Description | Required |
---|---|---|
name | ⚫ | |
description | ||
repository | Optional link to the backend repository. | |
version | SemVer compatible version number. | |
author | ||
license | ||
exec | Command that is executed when calling backend to perform some operation. | ⚫ |
build | Command that is executed to build the backend before bundling it. | ⚫ |
Each backend is built and included in the application so that it can be run in a predictable environment. The command build
defined in the configuration should build and copy executable and backend.yml
to the directory ./dist
which is the only directory that is bundled with the app. Command defined as exec
in the configuration is relative to this directory.
Backend is always called with operation as the first argument followed by an additional argument that differs between operations, e.g. ./backend operation
or ./backend operation file.pdf
. The backend SHOULD be stateless - that means it should not be influenced by any state it would save like info about what files it signed. All communication is in UTF-8 encoded plaintext. Following operations MUST be implemented:
-
meta
- Called to get info from the backend about its availability, supported file types, and options it supports. -
sign
- Called to sign the file with file path as an argument, e.g.sign file.pdf
. -
verify
- Called to verify the file signature with file path as an argument, e.g.verify file.pdf
.
The UI never sends anything over the STDIO unless it's asked to do so from the backend. That means the backend acts as a "master" and UI as a "slave". The UI can end the process if any unexpected situation arises and displays an error. The backend CAN, during operations sign
and verify
and at any moment:
- Write error to the STDERR that is immediately displayed.
- Prompt for additional information by writing to the STDOUT.
- Get the current value of any option by writing to the STDOUT.
Ending the process with the exit code 0 signals success. Some operations expect a result on the STDOUT before the process exits. If code other than 0 is used, the backend should always send an error message to the STDERR.
Messages exchanged via the STDIN and STDOUT (PROMPT, GETOPTION, RESULT) can be sometimes polluted by different libraries used on either part so they're distinguished from the rest by following the following format:
---TYPE---
any information exchanged in the appropriate format
---TYPE---
Such messages should be written in one chunk and flushed. Communication on the STDERR is accepted and displayed as-is.
The backend can ask for additional info by writing to the STDOUT message in the format:
--PROMPT--
type"Can you please something?"("Default answer is.")
--PROMPT--
The UI responds to that by writing a response to the STDIN in the format:
--PROMPT--
Response is something.
--PROMPT--
Type can be one of the following:
-
save
- Displays file picker and response is an absolute path. -
open
- Displays file picker and response is an absolute path. -
text
- Prompts for a text and response is an arbitrary string from the user. -
password
- Prompts for a password and response is an arbitrary string from the user. -
image
- Asks the user to draw or pick an image and the response is an absolute path. -
position
- Asks the user to position the image passed as an absolute path in the default value on the currently signed file. The response is a comma-separated -,
- set of coordinates in pixels, width in pixels, and page number:x,y,width,page
, e.g.150,450,80,1
, . Currently works only with PDFs.
Only one prompt can be made at a time.
The backend can ask for the current option value (modifiable on the Settings screen) by writing to the STDOUT message in the format:
--GETOPTION--
id
--GETOPTION--
The UI will respond by writing to the STDIN message like:
--GETOPTION--
response
--GETOPTION--
Where id
is from the list of options specified in the result of meta operation.
For example, the request is:
--GETOPTION--
keyPath
--GETOPTION--
And the response is:
--GETOPTION--
/home/user/.ssh/id_rsa
--GETOPTION--
Only one request can be made at a time.
Some operations should end with the result being written to the STDOUT before the process exits. It should follow the following format:
--RESULT--
Message in the agreed format
--RESULT--
Argument: none
Expected result on STDOUT: The backend MUST provide at least its status - whether it can be used on the current computer - on the first line. Additional information about supported file types or configurable options is OPTIONAL, on new lines.
Status MUST be "OK" if it can be used, any other string is used as an error message why it could not be used. This gives the backend a chance to check if it has all the necessary resources or if it supports the current platform, so it's clear it's ready to sign or verify documents.
Supported file types are OPTIONAL and MUST be a space-separated -
- list in the format SUPPORTS:item item
with each item being MIME type. If not specified, it's assumed the backend supports all file types. Supported types SHOULD be provided regardless of the status. They help the UI narrow down which backend should be used.
Options are OPTIONAL and MUST be a space-separated -
- list in the format OPTIONS:item item
with item in the format keyPath"Private RSA key"("~/.ssh/id_rsa")
. Options MUST be provided regardless of the status. Option values that will be filled by the user are currently assumed to be strings. These options are available on the Settings screen of the application.
Examples of communication:
The backend is called by executing ./backend meta
.
A. Example of result written by the backend to the STDOUT that says the backend can be used, it supports PDFs and Microsoft Word documents and can be configured using one option retrievable as keyPath
that is labeled "Private RSA key" on the Settings screen with default (autodetected) value "~/.ssh/id_rsa":
--RESULT--
OK
SUPPORTS:application/pdf application/msword
OPTIONS:keyPath"Private RSA key"("~/.ssh/id_rsa")
--RESULT--
The backend exits with status code 0.
B. Example of result written by the backend to the STDOUT that says the backend can't be used, that it supports all file types, and doesn't support any options:
--RESULT--
Required application Example could not be found. Please install it by visiting the site https://example.com.
--RESULT--
The backend exits with status code 0.
Argument: absolute path to the file
Expected result on the STDOUT: OPTIONAL absolute path to the output file.
Examples of communication:
The backend is called by executing ./backend sign /home/user/file.pdf
.
A. Example of a situation where the backend asks for a file path to the output file by writing to the STDOUT:
--PROMPT--
path"Output file"("/home/user/file.pdf")
--PROMPT--
And received the answer on the STDIN:
--PROMPT--
path"/home/user/file-signed.pdf"
--PROMPT--
After that, the backend exits with code 0 signaling success.
B. Example of a situation where the backend errors out by writing to the STDERR that the file is corrupted:
The provided file could not be signed as it is corrupted.
The backend exits with status code 1.
Argument: absolute path to the file
Expected result on the STDOUT: The first line MUST contain a string "SIGNED", "UNSIGNED", or "UNKNOWN" followed by an OPTIONAL description or additional info spanning from one to many following lines. Additional info supports basic markdown without support for HTML tags.
Examples of communication:
The backend is called by executing ./backend verify /home/user/file.pdf
.
A. Example of a situation where the backend can't verify whether the file is already signed or not without giving explanation by writing to the STDOUT result:
--RESULT--
UNKNOWN
--RESULT--
The backend exits with status code 0.
B. Example of a situation where the backend is able to verify the signature and provides detailed info by writing to the STDOUT result:
--RESULT--
SIGNED
**Signed by**: John Doe
**Date and time**: 01/01/1970 12:00 PM
Verified by the third party: **Acme Inc.**
--RESULT--
The backend exits with status code 0.
Backends can provide an optional file with translations that will be used for extraction (npm run extraction:translations
). This file has to be placed next to the backend.yml
, named translations.po
, and in gettext format with empty translations. It doesn't have to be copied during the building. Details of the operation verify
can also mark strings for translation - using t{Translate this}
means "Translate this" will be used for translation.