a cabal implementation in erlang
<!--
SPDX-FileCopyrightText: 2023 Henry Bubert

SPDX-License-Identifier: LGPL-2.1-or-later
-->

# CabErl

<!--[![builds.sr.ht status](https://builds.sr.ht/~cryptix/caberl.svg)](https://builds.sr.ht/~cryptix/caberl?)-->

https://nest.pijul.com/cryptix/caberl

A [cable]https://github.com/cabal-club/cable implementation in Erlang!

> **DISCLAIMER** This is my project to learn the language. Don't expect too much of plus it might not be fully idiomatic either.

## Done
- _fairly_ complete peer api
  - read & write channels, set_nick, set_topic, join & leave
- de- and encoding of messages and posts
- server listener, decode loop and request responses
- request channel state and posts for the last day
- request posts for hash replies

## TODO

- UTF-8 handling seems to be a bit gnarly
  - make sure all string fields (channels, text, topic, etc.) handle unicode codepoints properly
  - See https://www.erlang.org/doc/apps/stdlib/unicode_usage.html
- complete channel and connection state
  - new messages for open requests should be forwarded, etc. (TTL feature)
  - implement rate limiting based on bytes transmitted per peer
- complete tests for full remote peer lifecycle
  - cleanup peer socket linkage
- flesh out `db:read` with time windows
- add request for remote channel listings
- only fetch posts for hashes we actually asked for
- add supervisor tree for db and listener
- add more tests for links/heads tracking
- keep track of requests limits and time windows

## Building and testing

This project uses `rebar3` to build the erlang code and it's dependencies.

If you don't want to use the supplied [Nix]https://nixos.org/ `flake.nix`/`shell.nix` to get a development environment you need to install the following tools and libraries with your package manager of choice such that rebar can pick them up.


* [Rebar3]https://rebar3.org/
* [Erlang (25 or later)]https://www.erlang.org
* [libsodium]https://doc.libsodium.org
* [sqlite3]https://sqlite.org

If you are using archlinux, take a look at the `.build.yaml` for a list of package names.

To check if everything fetches and compiles you can use `rebar3 compile`, which should show something like this and exit cleanly:

```
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling cable
```

### Testing

The `rebar3 eunit` is a helper to run the [EUnit]https://www.erlang.org/doc/apps/eunit/chapter.html suites in the `test/` folder. This will spawn various peers and makes them comminicate in various ways.

### Headless peer in a shell

`rebar3 shell` starts an erlang shell with all dependencies loaded. You can use it to spawn one or more peers.

```erlang
1> {ok, Pid} = peer:start_link([]).
[Cable] Public Key: 75D98E54FE09B959EB7797D7F0F4FFE2E4F63EA735AADD1957FC00B2E3DCBA2B
[Transport] Loaded existing Curve25519 keypair
[DB] Path: "/home/cryptix/.caberl/sqlite.db"
[DB] schema.sql applied
[Peer] Event Loop: <0.404.0>
[Transport] Starting encrypted server on port 3113 (pid: <0.405.0>)
[Transport] Accept loop started (pid: <0.407.0>)
[Transport] Waiting for encrypted connections (listener: <0.406.0>, transport: <0.405.0>)
[Peer] Listening on {127,0,0,1}:3113
{ok,<0.400.0>}
2> Chan = "default".
2> peer:join(Pid, Chan).
ok
3> peer:write(Pid, Chan, "sup?").
[Wrote] #"welcome": "sup?"
ok
4> peer:dial(Pid, "some.ho.st:1234").
... communication logs
5> peer:stop(Pid).
```

To change the address and port for incoming connections, replace the `[]` in the `start_link([])` with `[{listener, ["1.2.3.4", 12345]}]`.

If you've never seen erlang before, a few tips:

* Statements need to be terminated with a `.`
* Variables need to be capitalized and can only be assigned once
* think of lower-case strings (like `ok` or `error`) like constants
* Exiting the shell is not very intutive. Press `ctrl+g` to get in the _User switch command_ mode. Here you can exit it by entering the letter q.

## Links

* Cabal website: https://cabal.chat/
* More Code https://github.com/cabal-club/

## License

Lesser GPL 2