Extensions
How a reader, a chunker, an embedding provider or a way of signing in is added from a folder without a change to this repository, allowed by hash, loaded from the bytes that were checked, and refused with a reason.
A reader, a chunker, an embedding provider or a way of signing in can be
added without a change to this repository. An extension is a folder holding
one assembly and an extension.json beside it:
{
"name": "sentence-chunker",
"version": "1.0.0",
"assemblyFile": "SentenceChunker.dll",
"sha256": "0a1b...",
"seams": { "chunker": 1, "reader": 1, "signin": 1 }
}
The folder goes under the extensions folder, which is the setting
extensions.folder or the environment variable PREM_EXTENSIONS_DIR, the
setting winning. Neither one means no extensions, which is not an error.
Nothing loads until an administrator allows it:
prem extensions allow /opt/premagentic/extensions/sentence-chunker prem extensions list prem extensions disallow sentence-chunker
allow reads the manifest, computes the assembly's SHA-256 itself, and
writes the name and that hash to the setting extensions.allowed, with an
entry in the change record. It refuses when the manifest's hash and the
assembly disagree, rather than writing a pair that could never match.
At startup the host reads the assembly once, hashes those bytes, and loads
the assembly from the same bytes, so the file cannot be exchanged between
the check and the load. The pair of name and hash must be in
extensions.allowed. An extension is refused, with a reason, when its
manifest cannot be read or used, when it names an assembly outside its own
folder, when the hash does not match, when nobody allowed the pair, when it
was built for a seam version this release does not offer, when it will not
run, or when what it registers is named the same as something already
loaded. A refusal never stops the deployment: it starts with the built-in
readers, chunkers, embedding providers and ways of signing in, and prem
extensions list and the health page say what was refused and why.
Each extension loads into its own context and takes the contracts it implements from the process, so two extensions can carry different versions of one library and neither can bring its own copy of the core. An extension may add an embedding provider under a new name and may not take a built-in one, so naming a provider can never quietly stand in for the local, offline model. An extension that brings a way of signing in is named in the log at every start, the way the trusted-header mode is.
An upgrade is a new assembly with a new hash, so it is allowed again on purpose. Changing either setting applies when the service is next started, which is also the only safe moment to replace an assembly on disk.
samples/extensions/sentence-chunker is a whole extension that registers a
chunker, a reader for .csv files and a sign-in adapter that claims nothing;
it is loaded in this repository's tests to prove the refusals and that each
seam is used end to end. tests/Premagentic.Conformance holds xunit fixtures
to inherit that prove a reader, a chunker, a connector or an embedding
provider keeps its contract; the built-ins are held to them here.