# lcagent - Librecast Multicast Agent

![Librecast Hex Logo](https://nlnet.nl/project/Librecast-OverlayMulticast/librecast.hex.svg)

<p>
<a href="https://scan.coverity.com/projects/lcagent">
  <img alt="Coverity Scan Build Status"
       src="https://scan.coverity.com/projects/31719/badge.svg"/>
</a>
</p>

**lcagent** is the [Librecast](https://librecast.net) multicast agent.

In server mode, lcagent can be configured to listen on Librecast Channels
(multicast groups) and execute programs in response to packet data received on
those Channels.

Packets must be accompanied by an authorized token and signed by the matching
key or they will be silently dropped.

Data is encoded with RaptorQ using Librecast’s [liblcrq](https://librecast.net/lcrq.html)
library to provide forward error correction in the event of packet loss.

Any data received in addition to the required token will be piped to stdin of
the configured commands after they are executed.  Multiple commands can be
executed sequentially.

lcagent can be used to send and receive data over multicast and to pipe data
between programs on one computer and as many receivers as the multicast network
can support simultaneously.

lcagent can be used for running CI builds on multiple build servers
simultaneously, triggered by multicasting the patch to all servers. Can also be
useful for monitoring and configuration management etc.

When an agent receives a command, it checks the supplied capability token
against its known trusted authorities before executing the command.

### CI Example

Each time a commit is made to a git repository, a patch can be generated and
sent to lcagent running on different CI test machines.  The patch will be
applied and the tests run. This is a simple example, but very complex CI systems
can be set up and controlled in this way.

Example `.lcagentrc`:

```
# this channel will apply a git patch sent to it which was created
# with git-format-patch(1).
# If the patch applies without error, ‘gmake test‘ is run
channel "lcagent patchtest"
        directory /home/lcagent/src/lcagent
        command "git am --committer-date-is-author-date"
        command "gmake test"
```

An example post-commit git hook which can be used to generate a patch suitable
for multicasting to the above Channel:

```
#!/bin/bash -eu

# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
# Copyright (c) 2025 Brett Sheffield <bacs@librecast.net>

FILE=`git format-patch HEAD~ --base HEAD~`
lcagent send "lcagent patchtest" - < $FILE

exit 0
```

## Options

\-d | \-\-debug
: Enable debug mode to print additional debugging information.

\-\-bpslimit rate[KMGT]
: When sending, this option limits the sending speed to rate.  Rate is a
number in bits per second (bps) and can be followed by a capital letter
representing SI units. eg. 100M limits the sending rate to 100 megabits per
second (Mbps).

\-\-expires seconds
: For the sign command, this sets the validity of the token in seconds.

\-f, \-\-follow
: In recv mode, do not stop after first data object received. Keep blocking and
receiving data until interrupted. eg. ˆC is pressed or SIGINT is received by the
process.

\-l, \-\-loopback
: Enable multicast loopback when sending. This will allow packets to be
received  by  the same  host  that sent them. By default multicast packets are
not received on the sending host.

\-\-overhead packets
: When sending, this option sets the number of additional repair packets sent to
aid with recovery of lost packets with RaptorQ encoding.  Default: 5

\-v, \-\-verbose
: Print more verbose output.

## Commands

### exec

`lcagent exec [options] channel [data] [-]`

The **exec** command executes the commands configured for *channel* locally, without
requiring data to be received over the network.

This is a good way to test the configured commands that will be executed before
making them accessible on the network.

The *nojoin* configuration directive can be used to ensure that a channel is
not network-executable, by preventing lcagent from joining the associated
Librecast channel. See lcagentrc(1).

### help

`lcagent help`

Print some (possibly) helpful instructions for lcagent.

### key

`lcagent key [options] add|del key`

Add or delete a key from the `authorized_keys` file.  Packets that do not include
a token signed by a key in the `authorized_keys` file are silently dropped.

### recv

`lcagent recv [options] channel`

Block and wait for data to be received on channel, where channel is a string
that will be hashed to create a Librecast channel.

Data received will be written to stdout. If expecting binary data, be sure to
redirect stdout somewhere appropriate.

By default, once EOF is received from the sender, the program will exit.  This
behaviour can  be changed by specifying the *\-\-follow* option.

### reload

`lcagent reload [options]`

Reload the lcagent configuration. Sends a SIGHUP to the daemon process.  Has
no effect unless lcagent has been started using the start command. Service files
should simply send SIGHUP to the process.

### send

`lcagent send [options] channel [payload] [-]`

Send data to channel, where channel is a string that will be hashed to create a
Librecast channel.

If payload is specified, this string will be sent as data to the channel.
If no payload is specified, an NULL (empty) payload of zero bytes will be sent
to the channel.

A hyphen (-) at the end of the command indicates that the payload should instead
be read from stdin.

### server

`lcagent [server] [options]`

Starting lcagent with no command starts it in server mode. The `.lcagentrc` file
will be parsed and lcagent will join the configured channels and wait for data,
executing the configured commands when packets are received on one of the
configured channels and writing any payload data to `stdin` of the command when
executing it.

See lcagentrc(1) for details of the runtime control (configuration) file.

### sign

`lcagent sign [options] [--expires seconds] bearer_key channel`

Create and sign a token for the given *bearer\_key* and *channel* and write to disk.
The  resulting token will be written to a file in the user’s state directory,
and the file pathname will be printed. If the token is copied to the state
directory of the bearer it will be used when sending to channel.

A signed token means that packets sent by the bearer will be accepted by any
receivers on chan‐ nel which have the signer’s key added to their
`authorized_keys` file. Tokens enable delegation of authority, which is
important for scaling. Using pre-signed tokens allows, say, a host to issue
tokens for virtual machines it creates without the need to add individual keys
for every machine to all receivers on the network.

In the absence of a signed token, packets will be sent with a self-signed token,
requiring that all receivers have the sender’s key installed in their
`authorized_keys` file.

If the expires option is supplied, this will be the validity in seconds of the
generated token.  A value of zero (0) means that the token will never expire.
By default tokens do not expire.

### start

`lcagent start [options]`

Start lcagent in server mode and fork it as a daemon.

Not intended for use with daemon supervising programs. If you are running
lcagent as a service, just  start lcagent with no command and let the init
system or daemon supervisor handle forking and pidfiles. If you are NOT running
lcagent as a service and want it to daemonize, use this command.

### stop

`lcagent stop [options]`

Stop the lcagent daemon.  Has no effect unless lcagent has been started using
the start com‐ mand. Sends SIGINT to the running daemon process.

### version

`lcagent version`

Print the version of lcagent and exit.

### whoami

`lcagent whoami [options]`

Print the active key to stdout and exit. This key can be added to the `authorized_keys`  file  of
another host with:
    `lcagent key add key`

## Configuration

See [lcagentrc](doc/lcagentrc.5.md) for details of the rcfile format.

## Mailing List

Join the low-traffic Librecast announce mailing list to stay up to date on
Librecast related news:

 https://lists.sr.ht/~librecast

## Supported Systems

lcagent and its Librecast dependencies are expected to build and run on:

- GNU/Linux (any distribution)
- FreeBSD
- NetBSD
- OpenBSD

If you encounter problems building or running on these systems, please send a
bug report. See *Bugs*.

If you get lcagent running on something else, please drop us an email and let us
know.

We'd love to know what you are using lcagent for and any feedback you have.

## Dependencies

**lcagent** requires liblibrecast (>= 0.11) to be installed and configured with liblcrq (>= 0.2.4)
and libsodium.

You will also need a C toolchain (lcagent is tested with gcc and clang). gnumake
is required on \*BSD systems (sorry).

Additional build dependencies:

- flex
- bison
- gmake

## Obtaining the source

lcagent (and the Librecast dependencies liblcrq and liblibrecast) can be
downloaded from Codeberg or SourceHut:

- https://codeberg.org/librecast/lcagent
- https://git.sr.ht/~librecast/lcagent

## Installation

```
git clone https://git.sr.ht/~librecast/lcagent
cd lcagent
./configure
make
make test
make install
```

## Files

˜/.lcagentrc
: This is the runtime control file for lcagent.  This is where global settings,
channels and  commands are defined that affect the runtime operation of the
program.  See lcagentrc(5) for more information.

˜/.local/state/lcagent/authorized\_keys
: This file contains the keys which are authorized to issue tokens which will be
accepted by the program when receiving data. One key is stored per line. It
can be managed with the lcagent key command.

## Security

Authenticity of data received is established by checking that each packet
received  contains a token signed  by an authority with their public key
installed in the `authorized_keys` file. If the token is valid, the packet must be
signed by the bearer key contained in the token.

Cryptographic signatures use the Ed25519ph algorithm provided by the libsodium library.

Data can be encrypted using the seed configuration directive in `.lcagentrc`. See [lcagentrc(5)](doc/lcagentrc.5.md).

The symmetric encryption key is generated from seed using BLAKE2b.

Symmetric encryption uses the XSalsa20 stream cipher from libsodium using a
Poly1305 MAC for authentication and random nonce.

If an encryption seed is used, the permissions for the rcfile must not be
world readable or lcagent will refuse to run and print an error.

## License

GPLv2 or (at your option) GPLv3

## Bugs

If you find one, email bugs@librecast.net or raise an issue in our online
bug tracker at: https://bugs.librecast.net/lcagent including as much relevant
information as you can.

## See Also

- [lcagent(1)](doc/lcagent.1.md)
- [lcagentrc(5)](doc/lcagentrc.5.md)

## Funding

This project was funded through [NGI Zero Core](https://nlnet.nl/core) and [NGI Assure](https://nlnet.nl/assure), funds established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/Librecast-OverlayMulticast).

[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" align="left"/>](https://nlnet.nl)
[<img src="https://nlnet.nl/image/logos/NGI0Core_tag.svg" alt="NGI Zero Core Logo" width="20%" align="right"/>](https://nlnet.nl/core/)
