Addon docs

This commit is contained in:
eikek
2022-05-16 15:01:28 +02:00
parent d077cc30cd
commit 5abbe92f2b
28 changed files with 1834 additions and 3 deletions

View File

@ -0,0 +1,11 @@
+++
title = "Addons"
insert_anchor_links = "right"
description = "Describes how addons work."
weight = 55
template = "pages.html"
sort_by = "weight"
redirect_to = "docs/addons/basics"
+++
No content here.

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View File

@ -0,0 +1,149 @@
+++
title = "Basics"
insert_anchor_links = "right"
description = "Docspell Addons."
weight = 10
template = "docs.html"
+++
# Addons
Addons allow to execute custom software within a defined context in
Docspell. The idea is to be able to support new features and amend
existing ones.
{% warningbubble(title="Experimental") %} Addons are considered
experimental. The interaction between addons and Docspell is still
subject to change.
The intended audience for addons are developers (to create addons) and
technically inclined users to install, configure and use them.
{% end %}
Despite the warning above, addons are a nice way to amend your
docspell server with new things, you are encouraged to try it out and
give feedback ;-).
{% infobubble(title="Enable addons manually") %}
Addons are disabled by default. They must be enabled in the config
file of the restserver!
{% end %}
## What is an Addon?
An addon is a zip file that contains a `docspell-addon.yml` (or .yaml
or .json) file in its root. The `docspell-addon.yml` is the *addon
descriptor* telling how to run and optionally build the addon. In the
ZIP file, an addon provides a program that expects one argument which
is a file containing the user input for the addon. Addons can
communicate back to docspell via their stdout and/or via directly
calling the docspell server as part of their program.
## What can Addons do?
Addons can accept user input and are arbitrary external programs that
can do whatever they want. However, Docspell can embed running addons
in restricted environments, where they don't have network for example.
Addons can safely communicate to Docspell via their stdout output
returning instructions that Docspell will realise.
Running addons is managed by docspell. Currently they can be executed:
- as the final step when processing or re-procssing an item. They then
have access to all the item data that has been collected during
processing (id, extracted text, converted pdfs, etc) and it can work
with that. It may, for example, set more tags or custom fields.
- trigger manually on some existing item
- periodically defined by a schedule. This executes the addons only
with the configured user input.
- … (maybe more to come)
Since an addon may not make sense to run on all these situations, it
must define a sensible subset via the `triggers` option in its
descriptor.
## How are they run
Addons are always executed by the joex component as an external
process, therefore they can be written in any programming or scripting
language.
That means the machine running joex possibly needs to match the
requirements of each addon. To ease this, addons can provide a [nix
descripton](https://nixos.wiki/wiki/Flakes) or a `Dockerfile`. Then
you need to prepare the machine only with two things (nix and docker)
to have the prerequisites for running many addons.
# More …
Addons are a flexible way to extend Docspell and require some
technical affinity. However, only "using" addons should not be that
hard, but it will always depend on the documentation of the addon and
its own complexity.
As the user, you may have different views: preparing the server to be
able to run addons, writing your own addons and finally using them
The following sections are divided these perspectives:
## Using Addons
Addons must be installed and then configured in order before they can
be used. [Using Addons](@/docs/addons/using.md) describes this
perspective.
{{ buttonright(href="/docs/addons/using", text="More…") }}
## Control how addons are run
As the owner of your server, you want to [control how addons are
run](@/docs/addons/control.md). Since addons are arbitrary programs,
potentially downloaded from the internet, they can be run in a
restricted environment.
{{ buttonright(href="/docs/addons/control", text="More…") }}
## Write custom addons
Finally, [writing addons](@/docs/addons/writing.md) requires (among
other things) to know how to interact with Docspell and what package
format is expected.
{{ buttonright(href="/docs/addons/writing", text="More…") }}
<!-- ## Goals -->
<!-- - Convenient for addon creators. Addons can be written in any -->
<!-- programming language and have a very light contract: they receive -->
<!-- one input argument and _may_ return structured data to instruct -->
<!-- docspell what to do. If not they can execute abritrary code to call -->
<!-- the server directly. -->
<!-- - Server administrators control how they are executed. Since addons -->
<!-- may run anything, the execution should be able to locked down when -->
<!-- wanted. -->
<!-- - Users can install and configure addons via the web interface easily. -->
<!-- It should be easy for addon creators to document how users can use -->
<!-- them. -->
<!-- # TODOs -->
<!-- - what if joex is running inside a container alread? -->
<!-- - some use cases: -->
<!-- - I want an addon to do some stuff when processing files -->
<!-- - my files named "something_bla" are always this specific document -->
<!-- and so very specific processing would be great -->
<!-- - I want XYZ files to work (e.g. mp3?) -->
<!-- - I want to generate previews for video files -->
<!-- - Example Addons: -->
<!-- - swiss qr code detection on invoices -->
<!-- - tags via regexes -->
<!-- - text extraction from audio? -->
<!-- - preview generation for video? -->

View File

@ -0,0 +1,238 @@
+++
title = "Control Runtime"
insert_anchor_links = "right"
description = "Control how addons are run"
weight = 30
template = "docs.html"
+++
# Control runtime of addons
Addons are run by the joex component as background tasks in an
external process. Depending on the machine it is running on, the addon
can be run
- inside a docker container
- inside a systemd-nspawn container
- directly on the machine
Addons can be provided as source packages, where the final program may
need to be built. They also can depend on other software. In order to
not prepare for each addon, it is recommended to install
[nix](https://nixos.org) with [flakes](https://nixos.wiki/wiki/Flakes)
and docker on the machine running joex.
Please also look at addon section in the [default
configuration](@/docs/configure/main.md#joex) for joex.
You need to explicitly enable addons in the restserver config file.
Docspell uses "runners" to execute an addon. This includes building it
if necessary. The following runner exist:
- `docker`: uses docker to build an run the addon
- `nix-flake`: builds via `nix build` and runs the executable in
`$out/bin`
- `trivial`: simply executes a file inside the addon (as specified in
the descriptor)
In the joex configuration you can specify which runners your system
supports.
## Prepare for *running* addons
Depending on how you want addons to be run, you need to install either
docker and/or systemd-nspawn on the machine running joex.
Additionally, the user running joex must be able to use these tools.
For docker it usually means to add the user to some group. For
systemd-nspawn you most likely want to configure `sudo` to run
passwordless the `systemd-nspawn` command.
Without this, an addon can only be run "directly" on the machine that
hosts joex (which might be perfectly fine). The addon then "sees" all
files on the machine and could potentially do harm.
It is recommended to install `nix` and `docker`, if possible. Addons
may only run with docker or only without, so supporting both leaves
more options.
## Prepare for *building* addons
Addons can be packaged as source or binary packages. For the former,
joex will build the addon first. There are two supported ways to do
so:
- via `docker build` when the addons provides a `Dockerfile` (use
runner `docker`)
- via `nix build` when the addon provides a `flake.nix` file (use
runner `nix-flake`)
Both build strategies will cache the resulting artifact, so subsequent
builds will be (almost) no-ops.
{% infobubble(title="Note") %}
*Building* addons requires to be connected to the internet! Running
them may not require a network connection.
{% end %}
If the addon is packaged as a binary, then usually the `trivial`
runner (possibly in combination with `systemd-nspawn`) can be used.
# Runtime
## Cache directory
Addons can use a "cache directory" to store data between runs. This
directory is not cleaned by docspell. If you have concerns about
space, use a cron job or systemd-timer to periodically clean this
directory.
## "Pure" vs "Impure"
Addons can talk back to Docspell in these ways: they can use the http
api, for example with [dsc](@/docs/tools/cli.md), or they can return
data to instruct Docspell to apply changes.
The former requires the addon to be connected to the network to reach
the Docspell *restserver*. This allows the addon to do arbitrary
changes at any time - this is the "impure" variant.
The second approach can be run without network connectivity. When
using docker or systemd-nspawn, Docspell will run these addons without
any network. Thus they can't do anything really, except return data
back to Docspell.
The pure way is much preferred! It allows for more consistent
behaviour, because Docspell is in charge for applying any changes.
Docspell can apply changes *only if* the addon returned successfully.
Addons can also be retried on error, because no changes happened yet.
It's the decision of the addon author, how the addon will work. It
should document whether it is pure or impure. You can also look into
the descriptor and check for a `networking: false` setting. As the
server administrator, you can configure Docspell to only accept pure
addons.
## Runners
### nix flake runner
For addons providing a `flake.nix` this runner can build it and find
the file to execute. With this `flake.nix` file addons can declare how
they should be build and what dependencies are required to run them.
The resulting executable can be executed via `systemd-nspawn` in a
restricted environment or directly on the machine.
{% infobubble(title="Requires") %}
You need to install [nix](https://nixos.org) and enable
[flakes](https://nixos.wiki/wiki/Flakes) to use this runner.
{% end %}
### docker
Addons can provide a Dockerfile or an image. If no image is given,
`docker build` will be run to build an image from the `Dockerfile`.
Then `docker run` is used to run the addon.
{% infobubble(title="Requires") %}
You need to install `docker` to use this runner.
{% end %}
### trivial
Addons can simply declare a file to execute. Docspell can use
`systemd-nspawn` to run it in an restricted environment, or it can be
run directly on the machine. This variant is only useful for very
simple addons, that don't require any special dependencies.
{% infobubble(title="Requires") %}
You need to check each addon for its requirements and prepare the
machine accordingly.
{% end %}
### Choosing runners
The config `addons.executor-config.runners` accepts a list of runners.
It specifies the preferred runner first. If an addon can be executed
via docker and nix, Docspell will choose the runner first in the list.
If you don't have nix installed, remove the `nix-flake` runner from
this list and same for docker, of course.
### systemd-nspawn
The `systemd-nspawn` can be used to run programs in a lightweight
ad-hoc container. It is available on most linux distributions (it is
part of systemd…). It doesn't require an image to exist first; this
makes it very convenient for running addons in a restricted
environment.
If you enable it in the config file, then all addons are either run
via `systemd-nspawn` or docker - and thus always in a restricted
environment, where they can only access their own files and the files
provided by Docspell.
The downside is that `systemd-nspawn` needs to be run as root (as far
as I know). Therfore, configure `sudo` to allow the user that is
running joex to execute `systemd-nspawn` non-interactively.
{% infobubble(title="Requires") %}
Install `systemd-nspawn` and enable the user running joex to use it
password-less via sudo.
{% end %}
# Within Docker
If joex itself is run as a docker container, things get a bit
complicated. The default image for joex does not contain `nix`, so the
`nix-flake` runner cannot be used out of the box.
In order to use the `docker` runner, the container must be configured
to access the hosts docker daemon. On most systems this can be
achieved by bind-mounting the unix socket (usually at
`/var/run/docker.sock`) into the container. Here is a snippet from the
provided `docker-compose` file:
```yaml
joex:
image: docspell/joex:latest
# ... left out for brevity
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /tmp:/tmp
```
Additionally to `/var/run/docker.sock`, it also bind mounts the `/tmp`
directory. This is necessary, because docker will be invoked with bind
mounts from inside the continer - but these must be available on the
host, because the docker client in the container actually runs the
command on the host.
The addon executor uses the systems temp-directory (which is usually
`/tmp`) as a base for creating a working and cache directory. Should
you change this in joex config file (or your system uses a different
default temp-dir), then the bind mount must be adapted as well.
Another variant is to extend the default joex image and add more
programs as needed by addons and then use the `trivial` runner.
# Summary / tl;dr
When joex is not inside a container:
- (optional) Install `systemd-nspawn` - it is provided on many
GNU/Linux distributions
- Configure `sudo` to allow the user running the joex component to
execute `systemd-nspawn` non-interactively (without requiring a
password)
- Install docker
- Install [nix](https://nixos.org) and enable
[flakes](https://nixos.wiki/wiki/Flakes)
- Allow the user who runs the joex component to use docker and nix. If
you install nix as multi-user, then this is already done.
- Check the section on addons in the [default
configuration](@/docs/configure/main.md#joex) for joex

View File

@ -0,0 +1,103 @@
+++
title = "Usage"
insert_anchor_links = "right"
description = "How to use addons"
weight = 20
template = "docs.html"
+++
# Using Addons
This shows with an example, how to install and use an addon. If the ui
doesn't show these forms, addons are probably disabled. Addons need to
be enabled in the config file of the rest server.
## Discovering
Addons can be installed from any URL to a zip file. One way is to use
URLs generated by forges like github or gitlab. They provide zip files
containing the repository contents. Alternatively an addon may provide
specific files in their release section.
For example, this is the url to the first release of the rotate-pdf
addon:
- <https://github.com/docspell/rotate-pdf-addon/archive/refs/tags/v0.1.0.zip>
This url points to a fixed version. It is also possible to use urls
that are "moving targets":
- <https://github.com/docspell/rotate-pdf-addon/archive/refs/heads/master.zip>
The contents behind the above url will very likely change over time.
For better discoverability, repositories for addons on public forges
can be tagged with *docspell-addon*.
## Install
With an URL like above, you can go to *Manage Data -> Addons -> New*
and insert the url:
{{ figure2(light="addon-install-01.png", dark="addon-install-01_dark.png") }}
It might take a while for Docspell to download, extract and verify the
addon. The addon will be downloaded into the database. Once installed,
the given URL is not used anymore, unless a manual update is issued.
After this finishes, you cannot change the URL anymore:
{{ figure2(light="addon-install-02.png", dark="addon-install-02_dark.png") }}
When using URLs pointing to "moving targets", you could click the
*Update Addon* button to re-download the contents at the url. This
doesn't make much sense for URLs to fixed versions (in *theory* these
could change as well, of course) and it is not without risk. It can be
useful for own addons to have them quickly updated.
Now the addon is installed. It can now be used by creating a *run configuration*.
## Run Configuration
A run configuration is comprised of one or more addons, their inputs
and some settings regarding their runtime environment.
The name is used for displaying in the webapp. You can disable/enable
a run configuration.
It is possible that addons use [dsc](@/docs/tools/cli.md) or call the
rest-server otherwise. Usually a valid session is required (to set
tags or do searches). When selecting to run *on behalf of a user*, a
valid authenticator for that user is injected into the environment of
the addon run.
The *Trigger Run* setting specfies when this run configuraiton should
be executed. You can choose from options that all addons in the list
must support. In this example, only `existing-item` is used. This
means the run configuration can be selected to run on any item.
Other options include:
- `final-process-item`: executes automatically as the last step when
processing uploaded files
- `final-reprocess-item`: like `final-process-item` but applies when
an existing item is reprocessed.
- `scheduled`: runs periodically based on a schedule (and independent
from any item)
Each addon may require arguments. Click on *Configure* to enable the
*Arguments* section and add arguments for the corresponding addon.
What to insert here is completely specific to the addon. In this case,
it expects a JSON object with only one field `"degree"` that indicates
how to rotate. In this example, it should be rotated by 90°
counter-clockwise. You need to click *Update* to set it into the addon
and then *Submit* to save everything.
{{ figure2(light="addon-install-03.png", dark="addon-install-03_dark.png") }}
With this run configuration in place, you can try it out on some item:
{{ figure2(light="addon-install-04.png", dark="addon-install-04_dark.png") }}
This example configured the *rotate-pdf-addon* to rotate left by 90°.
Create a simlar run configuration to rotate to the right.

View File

@ -0,0 +1,376 @@
+++
title = "Writing"
insert_anchor_links = "right"
description = "How to write addons"
weight = 20
template = "docs.html"
+++
# Writing Addons
Writing an addon can be divided into two things:
- create the program
- define how to package and run it
The next sections describe both parts. For a quick start, check out
the example addons.
As previously written, you can choose a language. The interaction with
docspell happens by exchanging JSON data. So, whatever you choose, it
should be possible to read and produce JSON with some convenience.
# Writing the program
## Interface to Docspell
The interface to Docspell is JSON data. The addon receives all inputs
as JSON and may return a JSON object as output (via stdout).
An addon can be executed in different contexts. Depending on this, the
available inputs differ. The addon always receives one argument, which
is a file containing the user supplied data (it may be empty). A user
is able to provide data to every addon from the web-ui.
All other things are provided as environment variables. There are
environment variables that are always provided and some are only
available for specific contexts.
For example, an addon that is executed in the context of an item
(maybe after processing or when a user selects an addon to run "on an
item"), Docspell prepares all data for the corresponding item and
makes it available to the addon. In contrast, an addon executed
periodically by a schedule, won't have this data available.
## Basic Environment
The following environment variables are always provided by Docspell:
- `ADDON_DIR` points to the directory containing the extracted addon
zip file
- `TMPDIR` / `TMP_DIR` a directory for storing temporary data
- `OUTPUT_DIR` a directory for storing files that should be processed
by docspell
- `CACHE_DIR` a directory for storing data that should stay between
addon runs
It is very much recommended to always use these environment variables
when reading and writing data. This keeps Docspell in control about
the exact location.
The working directory will be set to a directory that is also
temporary, but please don't rely on that. Use the environment
variables.
## Item data
When executed in the context of an item. Meaning for triggers:
`final-process-item`, `final-reprocess-item`, `existing-item`.
### `ITEM_DATA_JSON`
This environment variable points to a JSON file containing information
about the current item. If it is run at processing time, it includes
all information gathered so far by Docspell.
**Example**
{{ incl_json(path="templates/shortcodes/item-data") }}
### `ITEM_ARGS_JSON`
This environment variable points to a JSON file that contains the user
supplied information with an upload request. That is, a user may
specify tags or a language when uploading files. This would be in this
file.
*This is only available for uploads. Trigger `final-process-item`.*
**Example**
{{ incl_json(path="templates/shortcodes/item-args") }}
### `ITEM_ORIGINAL_JSON` and `ITEM_PDF_JSON`
These JSON files contains a list of objects. Each object provides
properties about a file - either the original file or the converted
pdf. The structure is the same.
**Example**
{{ incl_json(path="templates/shortcodes/file-meta") }}
### Directories
These environment variables point to directories that contain the attachment files.
- `ITEM_PDF_DIR` contains all converted pdf files, the attachment id is the filename
- `ITEM_ORIGINAL_DIR` contains all original files, the attachment id is the filename
For example, to obtain a converted pdf file, lookup the id in
`ITEM_PDF_JSON` and then construct the file name via
`ITEM_PDF_DIR/{id}`.
## Session for dsc
An addon may use [dsc](@/docs/tools/cli.md) which requires for many
commands a valid session identifier. Usually this is obtained by
logging in (i.e. using `dsc login`). This is not really feasible from
inside an addon, of course. Therefore you can configure an addon to
run on behalf of some user when creating the run configuration.
Docspell then generates a valid session identifier and puts it into
the environment. The [dsc](@/docs/tools/cli.md) tool will pick them up
automatically.
It will also setup the URL to connect to some restserver. (If you have
multiple rest-servers running, it will pick one randomly).
- `DSC_SESSION` env variable containing a session identifier. It's
validity is coupled on the configured timeout.
- `DSC_DOCSPELL_URL` the base url to some rest server
That means when using an addon in this way, you can simply use `dsc`
without worrying about authentication or the correct URL to connect
to.
## Output
Docspell doesn't interpret the returncode of an addon, except checking
for being equal to `0` which indicates a successful run.
In order to do change data in Docspell, the addon program can run
`dsc` (for example) to change some state - like setting tags etc. But
the preferred approach would be to return instructions for Docspell.
Docspell will execute the instructions when the addon terminates
successfully - that is with return code `0`.
These instructions are in a JSON object which needs to go to stdout.
You can use stderr in an addon for logging/debugging purposes. But if
you specify `collectOutput: true` in the descriptior, then stdout must
only return this specific JSON (or nothing, empty output is ignored).
You find the complete structure below. It consists of these parts:
- `commands`: let's you declare actions to do for an item or attachment
- `files`: defines files relative to `OUTPUT_DIR` that should be
processed
- `newItems`: declares files relative to `OUTPUT_DIR` that should be
processed as new uploads
The `commands` allows to set tags, fields and other things. All parts
are optional, you don't need to return the complete structure. Just
returning `commands` or only `files` is ok.
**Example**
{{ incl_json(path="templates/shortcodes/addon-output") }}
# Descriptor
An addon must provide an *addon descriptior*, which is a yaml or json
file looking like this:
```yaml
# The meta section is required. Name and version must not contain
# whitespace
meta:
name: "name-of-addon"
version: "2.21"
description: |
Describe the purpose and how it must be used here
# Defining when this addon is run. This is used to guide the user
# interface in selecting an addon. At least one is required to specify.
#
# Possible values:
# - scheduled: requires to enter a timer to run this addon periodically
# - final-process-item: the final step when processing an item
# - final-reprocess-item: the final step when reprocessing an item
# - existing-item: A user selects the addon to run on an item
triggers:
- final-process-item
- final-reprocess-item
- existing-item
# How to build and run this addon (optional). If missing, auto
# detection will enable a nix runner if a `flake.nix` is found in the
# source root and docker if a `Dockerfile` is found.
#
# Both runners are compared to what is enabled at the server.
runner:
# Building the program using nix flakes. This requires a flake.nix
# file in the source root with a default package and a flake-enabled
# nix on the joex machine.
#
# The program is build via `nix build`. If the joex machine has
# systemd-nspawn installed, it is used to run the addon inside a
# container. Otherwise the addon is run directly on the machine.
nix:
enable: true
# Docker based runner can define a custom image to use. If a `build`
# key exists pointing to a Dockerfile, the image is build before. If
# the docker image is complex, you can build it independently and
# provide the pre-build image.
#
# The program is run via `docker run` passing the arguments to the
# addon. Thus it expects the entrypoint to be correctly configured
# to the executable. You may use `args` in order to prepend
# additional arguments, like the path to an executable if the image
# requires that. The joex machine must have docker installed and the
# user running joex must be allowed to use docker. You must either
# define an image with an appropriate entry point or a dockerfile.
docker:
enable: false
#image: myorg/myimage:latest
build: Dockerfile
# Trivial runner that simply executes the file specified with
# `exec`. Nothing is build before. This runner usually requires that
# the joex machine contains all dependencies needed to run the
# addon. You may need to install additional software on the machine
# running joex.
trivial:
enable: false
exec: src/addon.sh
# Optional arguments/options given to the program. The program
# receives at least one argument, which is a file to the user input as
# supplied in the application. The arguments here are prepended.
args:
options:
# If false, the program is run inside a private network, blocking
# traffic to the host and networks reachable from there. This only
# applies if the addon can be run inside a container.
#
# If the addon runs side effects (such as using dsc to set tags),
# this must be set to `true`.
#
# Default is false.
networking: true
# If true, the stdout of the program is parsed into a JSON structure
# that is interpreted as actions executed by the task that runs the
# addon. If the addon runs side effects only, set this to `false`
# and the output is ignored.
#
# It is recommended to use this approach, if possible. It allows
# docspell itself to apply any changes and the addon can run
# completely isolated.
#
# Default is false.
collectOutput: true
```
# Packaging
Docspell can use different ways to build and run the addon:
`nix-flake`, `docker` and `trivial`. The first two allow to package
the addon in a defined way (with a single dependency, either nix or
docker) and then execute it independently from the underlying system.
This makes it possible to execute the addon on a variety of systems.
This is especially useful for addons that are meant to be public and
reusable by different people.
The "trivial" runner is only executing some program specified in
`docspell-addon.yaml`, directly on the joex machine (or via
`systemd-nspawn`). The machine running joex must then provide all
necessary dependencies and it must be compatible to run the addon. It
may be useful especially for personal addons.
## nix flake
Using [nix](https://nixos.org) with
[flakes](https://nixos.wiki/wiki/Flakes) enabled, is the recommended
approach. It is very flexible and reproducible while sharing most
dependencies (in contrast to docker where each image contains the same
packages again and again).
Docspell runs `nix build` to build the addon and then executes the
file produced to `$out/bin`.
## docker
For docker it is recommended to provide pre-build images. Docspell can
build images from provided `Dockerfile`, but for larger images it
might be better to do this apriori.
Docspell will run the addon using `docker run …` passing it only the
user-input file as argument. Thus the image must define an appropriate
`ENTRYPOINT`.
# Examples
## Minimal Addon
The steps below create a minimal addon:
1. Create a bash script `addon.sh` with this content:
```bash
#!/usr/bin/env bash
echo "Hello world!"
```
2. Make it executable:
```bash
chmod +x addon.sh
```
3. Create a yaml file `docspell-addon.yaml` with this content:
```yaml
meta:
name: "minimal-addon"
version: "0.1.0"
triggers:
- existing-item
- scheduled
runner:
trivial:
enable: true
exec: addon.sh
```
4. Create a zip file containing these two files:
```bash
zip addon.zip docspell-addon.yaml addon.sh
```
The addon is now ready. Make it available via an url (use some file
sharing tool, upload it somewhere etc) and then it can be installed
and run.
## Non-Minimal Addon
The minimal example above is good to see what is required, but it is
not very useful…. Please see this post about the [audio file
addon](@/blog/2022-05-16_audio_file_addon.md) that walks through a
more useful addon.
# Misc
## Advantages of "pure" addons
Although the output structure is not set in stone, it is recommended
to use this in contrast to directly changing state via `dsc`.
- outputs of all addons are collected and only applied if all were
successful; in contrast side effects are always applied even if the
addon fails shortly after
- since addons are executed as joex tasks, their result can be send as
events to another http server for further processing.
- addons can run in an isolated environment without network (no data
can go out)
## Use addons in other addons?
This can be achieved very conveniently by using `nix`. If addons are
defined as a nik flake, they can be easily consumed by each other.