"Load every .so on the path" means anything that can drop a file in that directory can run code in your process. plugin-sign narrows that: an add-on carries an Ed25519 signature, and the host checks it against a set of trusted public keys before — or instead of — trusting the code.
This half of the toolkit needs OpenSSL. Build without it and DSOLoader still has plain load(); the signed overloads and the tools are simply absent.
The workflow
Once, per developer — make a key pair:
dso-keygen --name "Alice" --email "alice@example.com" --key-id alice --out-dir keys/
# keys/alice.key private (chmod 400)
# keys/alice.pub public
# keys/alice.fingerprint SHA-256 of the DER public key
Per build — sign the DSO:
dso-sign libCircle.so --key keys/alice.key --name circle-plugin --version 1.2.0
dso-sign computes the SHA-256 of the file as built, writes a JSON manifest (name, version, author, that hash, the signer's key fingerprint, an ABI version, a timestamp), Ed25519-signs the manifest, and embeds both as ELF sections (.dso_manifest, .dso_sig) with objcopy. Re-signing is idempotent — old sections are stripped first, and the hash is over the pre-section bytes so it stays stable.
Once, per deployment — trust the key on the host:
cp keys/alice.pub /etc/app/trusted-keys.d/
Any *.pub in that directory is a trusted signer.
Checking at load time
Two overloads, both requiring the plugin-sign build:
#include <dso/DSOLoader.hh>
#include <sign/TrustLevel.hh>
using namespace fedem;
// (a) Load, then report — the loader never blocks, the caller decides
auto r = dso::DSOLoader::loadSigned( "libCircle.so",
{ "/opt/app/plugins" },
"/etc/app/trusted-keys.d" );
if( !r.loaded ) throw std::runtime_error( r.detail );
switch( r.trust ) {
case sign::TrustLevel::TRUSTED: break; // ok
case sign::TrustLevel::UNKNOWN: warn( "unrecognised signer" ); break;
case sign::TrustLevel::UNSIGNED: warn( "unsigned add-on" ); break;
case sign::TrustLevel::REJECTED: unload_and_abort( r.detail ); break;
}
// (b) Load only above a trust floor — throws SignatureRejected otherwise
dso::DSOLoader::loadVerified( "libCircle.so",
{ "/opt/app/plugins" },
"/etc/app/trusted-keys.d",
sign::TrustLevel::TRUSTED );
loadSigned is for a host that wants to log or warn but still run. loadVerified is for "no unsigned code, full stop" — pick the minTrust (TRUSTED in production, maybe UNKNOWN in staging) and a failure is an exception, not a branch.
Trust levels
| Level | Meaning | Typical action |
|---|---|---|
TRUSTED | valid signature, signer key in the trusted-keys dir | load |
UNKNOWN | valid signature, signer key not in the dir | warn, or reject in prod |
UNSIGNED | no manifest / signature sections | dev: load; prod: reject |
REJECTED | signature invalid, or file hash ≠ manifest hash | always reject |
REJECTED specifically means the file was tampered with after signing, or signed by a key that does not match the manifest — treat it as hostile.
The per-process cache
Verifier::verify() caches its result by path. dlopen pins the inode, so the on-disk file can change without affecting the running process — one verification per path is both correct and cheap. Verifier::isCached(path) lets a host suppress repeat log lines.
String-policy helper
If your host reads a policy from config as a string:
bool ok = fedem::sign::Verifier::shouldLoad( policy, result.trust );
// "off" -> always true
// "warn" -> true unless REJECTED
// "strict" -> true only for TRUSTED
Full flag-by-flag detail for the tools is in the Tools section. Next: Packaging Plugins.

