Using SAL Modules
A SAL Module is a git repository with a Dockerfile in its root whose
container implements the SAL Module command line interface.
Referencing one from your project’s RDF lets sal build run it and fold the RDF it produces into your data product.
Reference a module
Section titled “Reference a module”A module is referenced through the salmodule:// protocol scheme.
Declare it as a prefix and type an instance with one of the classes the module’s ontology defines:
@prefix salmodule: <https://w3id.org/sal/cgs-earth/sal-module-spec/salmodule#> .@prefix states: <salmodule://github.com/cgs-earth/python-geoconnex/> .@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<GeoconnexStates> a states:GeoconnexReferenceFeatureStates ; a salmodule:NodeProcessor ; states:maxRetries "5"^^xsd:integer .The prefix takes the form salmodule://[HOST/]OWNER/REPO/.
HOST is optional and defaults to github.com.
The prefix must end in a slash
Section titled “The prefix must end in a slash”A module’s ontology declares its classes as relative IRIs, such as "@id": "GeoconnexReferenceFeatureStates".
SAL resolves them against the prefix you declared, and only a slash-terminated prefix places them underneath
the module:
# correct@prefix states: <salmodule://github.com/cgs-earth/python-geoconnex/> .# states:GeoconnexReferenceFeatureStates and the ontology's own term both become# salmodule://github.com/cgs-earth/python-geoconnex/GeoconnexReferenceFeatureStates
# wrong@prefix states: <salmodule://github.com/cgs-earth/python-geoconnex#> .# relative IRI resolution drops the last path segment, so the ontology's term becomes# salmodule://github.com/cgs-earth/GeoconnexReferenceFeatureStates# which no longer matches the term the prefix expands toA prefix that does not end in a slash reports every module term as undefined even though the module and its ontology are fine, so reach for the slash-terminated form whenever a module’s terms unexpectedly fail to validate.
Configure the task
Section titled “Configure the task”A task is configured in RDF, with the properties the module’s own vocabulary defines.
states:maxRetries above is one of them, so the module decides what a valid task instance looks like
and sal validate reports a typo in a configuration property the same way it reports a typo in a class.
SAL serializes those properties into the JSON-LD node object that salmodule:runCommand is invoked with.
The instance above is passed as:
{"@id":"https://example.org/GeoconnexStates","@type":"GeoconnexReferenceFeatureStates","maxRetries":{"@value":"5","@type":"xsd:integer"}}The rules SAL follows when writing it:
- Only properties in the module’s own namespace become keys. Everything else the project says about the
instance, such as an
rdfs:labelor aschema:name, is left out, since only the module knows what configures it. - Keys and IRIs are written the way the module’s ontology writes them: a term of the module’s vocabulary
becomes its relative name, and any other IRI is shortened with whichever prefix the ontology’s
@contextbinds it to. - A typed literal becomes a
{"@value": ..., "@type": ...}object and a language-tagged literal becomes a{"@value": ..., "@language": ...}object. A plain string is written as a string. - A property used more than once becomes an array.
- A blank node is inlined as a nested object, so structured configuration can be expressed with the usual
turtle
[ ... ]syntax. A named node stays a{"@id": ...}reference.
Which ontology property describes what
Section titled “Which ontology property describes what”Two properties of the SAL Module ontology are involved, and it is worth keeping them apart:
salmodule:runCommanddescribes the invocation the task instance is an input to, and so is the property that documents the JSON-LD node object above. Itsskos:noteis what says the value must be a JSON-LD representation of thesalmodule:Tasksubclass instance being invoked.salmodule:taskInstanceEnvVaronly defines a constant: the name of the environment variable that value is carried in. A module declares it on its own ontology, and it defaults toSALMODULE_TASK_INSTANCE.
What SAL does with it
Section titled “What SAL does with it”Validation dereferences the salmodule:// prefix so that the module’s terms are checked like any other vocabulary.
SAL clones the repository, builds the Dockerfile in its root, and runs
docker run IMAGE salmodule ontology to read the module’s vocabulary.
A typo in a module term is reported the same way a typo in a schema: term is:
data/module.ttl:5: undefined term states:GeoconnexReferenceFeatureStatesssal build then runs each task instance with docker run -e SALMODULE_TASK_INSTANCE=... IMAGE salmodule run.
An instance is only run when it is typed as a SAL Module task, either because the project’s RDF types it as a
salmodule:NodeProcessor, salmodule:NodeProducer, salmodule:NodeConsumer, or salmodule:Task, or because
the module’s own ontology declares its class as a subclass of one of those.
A task writes newline delimited JSON to stdout.
SAL injects the module ontology’s @context into each node so that its keys resolve to the module’s vocabulary,
converts the result to RDF, and merges it into the graph being built.
Those triples land in the Iceberg table alongside the triples from your source files.
If a task emits salmodule:Error nodes instead of data, the build fails with the messages the module reported.
Modules recorded in the table
Section titled “Modules recorded in the table”Every module a build downloaded, whether it was dereferenced for its vocabulary or run as a task, is written to
the Iceberg table metadata as a JSON list under the sal.salmodules property:
["salmodule://github.com/cgs-earth/python-geoconnex"]The list is rewritten on every build, so dropping a module from your RDF drops it from the table too.
sal query --info properties prints it, and the Modules tab of sal serve --with-ui offers each entry as
a chip and as an autocompletion so a module the table was built from can be inspected without typing its URI.
Requirements
Section titled “Requirements”- A reachable docker daemon. SAL talks to whatever
DOCKER_HOSTpoints at, defaulting to the local socket. gitonPATH, used to clone the module repository.
Each module is cloned and built once per sal build invocation.
Dereferenced module ontologies are cached alongside SAL’s other vocabularies; --no-cache clears that cache.