A FederationLib plugin that classifies scanned content with BayesianServer and trains it with the classifications assigned to evidence. It provides FederationLib's Bayesian classification and training, which used to be built into FederationLib.
The plugin does two things: it classifies the content FederationLib scans, and it learns from the classifications operators give to evidence.
Classifying content. When content is scanned with POST /scan, every evidence item that has text is sent to
BayesianServer and classified as NORMAL, SUSPICIOUS or MALICIOUS. FederationLib takes it from there: the
classification appears in the scan's classification, counts towards the risk score through the CLASSIFICATION_*
scanning rules, and describes the evidence of reports that are generated automatically from a scan. The scan
request's top_k and threshold parameters are passed on to BayesianServer.
Learning from classifications. Whenever evidence is given a classification, its text is sent to BayesianServer to train the model. This happens when:
- evidence is submitted with a classification,
- evidence is classified with
PATCH /evidence/{uuid}/classify, - a report is closed with a classification.
Learning can be turned off with the learning option.
When content isn't classified. The plugin would rather not classify content than guess. It skips the classification when:
- the model hasn't been trained enough yet, it needs at least
minimum_documentsdocuments in total and for each ofNORMAL,SUSPICIOUSandMALICIOUS, - the model contains a label the plugin doesn't know,
- most of the content's words are unknown to the model (
classify_known_tokens).
The confidence of a classification is the probability BayesianServer gives its top label.
If BayesianServer is unavailable, scans and other operations still succeed, the content just isn't classified and the error is logged.
Direct access to BayesianServer. When the proxy option is enabled, the root operator can use BayesianServer's
API through FederationLib, every request below /bayesian is passed on to BayesianServer as-is and its response is
returned unchanged:
| FederationLib | BayesianServer |
|---|---|
GET /bayesian/ |
GET / (status) |
POST /bayesian/ |
POST / (classify) |
PUSH /bayesian/ |
PUSH / (train) |
GET /bayesian/health |
GET /health |
POST /bayesian/analytics |
POST /analytics |
The request's method, query string, body and Content-Type are forwarded, BayesianServer's status code,
Content-Type and body are returned. Any other operator is refused (403), and anonymous requests need to
authenticate (401). The operator's access token is never sent to BayesianServer.
The proxy is disabled by default, since it gives direct control over the model, including training it without
classified evidence. While disabled, /bayesian responds like a path that doesn't exist.
| Handler | Handles |
|---|---|
ContentScanHandler |
CONTENT_SCAN, classifies scanned content |
EvidenceClassifiedHandler |
RECORD_CHANGE (EVIDENCE_CLASSIFIED), trains the model |
BayesianProxyHandler |
The /bayesian/* route, proxies BayesianServer's API |
The plugin has its own ConfigLib configuration named bayesian_plugin, every value can also be set with its
environment variable. The defaults connect to the BayesianServer bundled with the FederationLib docker image.
| Name | Environment Variable | Type | Default Value | Description |
|---|---|---|---|---|
host |
BAYESIAN_PLUGIN_HOST |
string | 127.0.0.1 |
The host of BayesianServer |
port |
BAYESIAN_PLUGIN_PORT |
integer | 6380 |
The port of BayesianServer |
ssl |
BAYESIAN_PLUGIN_SSL |
boolean | false |
Connect to BayesianServer with HTTPS |
classify_known_tokens |
BAYESIAN_PLUGIN_CLASSIFY_KNOWN_TOKENS |
boolean | true |
Skip the classification when the majority of the content's tokens are unknown |
minimum_documents |
BAYESIAN_PLUGIN_MINIMUM_DOCUMENTS |
integer | 10 |
Training documents required in total and per label before content is classified |
learning |
BAYESIAN_PLUGIN_LEARNING |
boolean | true |
Train BayesianServer with the text content of classified evidence |
proxy |
BAYESIAN_PLUGIN_PROXY |
boolean | false |
Proxy BayesianServer's API below /bayesian for the root operator |
The plugin is loaded by FederationLib through the plugins configuration (FEDERATION_PLUGINS) using its package
name, net.nosial.bayesian_plugin. FederationLib (and ConfigLib and LogLib2) are provided by FederationLib at runtime.
make target/release/net.nosial.bayesian_plugin.ncc
ncc install --package="$PWD/target/release/net.nosial.bayesian_plugin.ncc" --yes --reinstallThe FederationLib docker image installs plugins listed in REQUIRE_PLUGINS before it initializes:
environment:
- REQUIRE_PLUGINS=/opt/plugins/net.nosial.bayesian_plugin.ncc # Anything `ncc install` accepts
- FEDERATION_PLUGINS=net.nosial.bayesian_pluginNo migration is needed, FederationLib includes this plugin as one of the default plugins, the plugin uses the same BayesianServer and model, nothing needs to be retrained.
This plugin does not conflict with any other plugin by default.
make # Builds target/release and target/debug
make test-env # Starts the test environment (FederationLib with the plugin installed, see below)
make test # Runs the tests (phpunit) against the test environment, requires the release build
make test-env-down # Removes the test environment and its dataThe tests run against a live FederationLib server with the plugin installed, started with Docker Compose
(docker-compose.yml):
| Service | Description | Port |
|---|---|---|
app |
FederationLib's published dev image (ghcr.io/nosial/federationlib:dev) with the plugin installed |
7000 (FEDERATION_PORT) |
mariadb |
FederationLib's database | - |
redis |
FederationLib's cache | - |
The Dockerfile builds the plugin from source and adds it to FederationLib's image. FederationLib's entrypoint
installs it and enables it through REQUIRE_PLUGINS and FEDERATION_PLUGINS, with its proxy option enabled. The
BayesianServer bundled with FederationLib's image isn't published, the tests reach it through the plugin's /bayesian
proxy as the root operator. The image is only used for testing and is never published.
make test-envbuilds with--pull, so the tests always run against the latest build of FederationLib'sdevimage rather than an outdated local copy. It then waits until FederationLib and BayesianServer (through the proxy) respond.- The plugin inside the container is the one built when the image was built, not
target/release. Runmake test-envagain after changing the plugin, otherwise the tests against the server use the old plugin. - Nothing is persisted:
make test-env-downremoves the database and the trained model.
The tests import FederationLib from the installed net.nosial.federation package. They're run against FederationLib's
dev branch, so the plugin is always tested against the latest working build of the server. Install FederationLib from
a checkout of that branch (its dependencies are installed with it), then the plugin package:
git clone --branch dev https://github.com/nosial/federationlib
(cd federationlib && ncc build --configuration release && ncc install --package="$PWD/target/release/net.nosial.federation.ncc" --yes --reinstall)
make target/release/net.nosial.bayesian_plugin.ncc
ncc install --package="$PWD/target/release/net.nosial.bayesian_plugin.ncc" --yes --reinstallThe tests are configured by phpunit.xml, an environment variable that is already set takes precedence:
| Environment Variable | Default | Description |
|---|---|---|
SERVER_ENDPOINT |
http://172.17.0.1:7000 |
The FederationLib server of the test environment |
SERVER_ACCESS_TOKEN |
abcdefghijklmnopqrstuvwxyz123456 |
The root operator's access token (FEDERATION_ACCESS_TOKEN), for /bayesian |
NCC_BUILD_OUTPUT_PATH |
target/release/... |
The plugin .ncc package to import |
The endpoints use the Docker host's bridge address (172.17.0.1), the same as FederationLib's tests, so they work
from the host and from a CI job's container. Where the bridge isn't reachable (eg; Docker Desktop), use
http://127.0.0.1:7000. The tests train the BayesianServer, so only run them against the test environment. A test
fails rather than being skipped if the server is unreachable.
This project is licensed under the MIT License - see the LICENSE file for details.