Skip to content

Commands

SAL commands operate on a Git-backed project and the generated data under .sal/data. For a step-by-step flow, start with Getting Started.

The sal help output, listing every subcommand

Initializes a SAL project in the current Git repository. The repository must not be your home directory and must already have a Git remote.

sal init creates:

  • .sal/data in the project for generated data.
  • ~/.sal/cache for user-level cache data.
  • ~/.sal/config.jsonld for user-level SAL configuration.
  • A .gitignore entry for .sal/data.

sal init running in a repository that already has a remote

Validates RDF source files without committing a new data product. Use it while editing Turtle or JSON-LD files:

Terminal window
sal validate data/

Important flags:

  • --prefix-maps: apply prefix mappings as source=target entries or source/target pairs.
  • --format: choose iceberg or nq; defaults to iceberg.
  • --no-cache: clear SAL’s temp cache before validating, while still caching new fetches during the run.

An undefined term is reported with the file and the line number it appears on:

sal validate passing on one directory and reporting an undefined term in another

Validating a file that references a SAL module clones and builds that module so its vocabulary can be checked. sal validate does not run module tasks; only sal build does.

Validates RDF source files and writes the merged graph into the local data product.

Terminal window
sal build data/

By default, sal build requires a clean Git working tree. Use --force only for test or debugging builds.

Important flags:

  • --prefix-maps: apply prefix mappings before validation.
  • --format: choose iceberg or nq; defaults to iceberg.
  • --typed: split distinct RDF data types into separate output columns.
  • --force: build even when the Git working tree has uncommitted changes.
  • --no-cache: clear SAL’s temp cache before building, while still caching new fetches during the run.

Only triples that are not already in the table are committed, so rebuilding unchanged sources creates no new snapshot:

sal build writing nine triples, then reporting no changes on a second run

sal build also materializes any SAL module the source files reference. It clones each referenced module, builds the Dockerfile in its repository root, runs the task instance declared in the RDF, and merges the triples the module produced into the data product. See Using SAL Modules.

See Data Layout for the generated table structure.

Opens an interactive SQL shell with a triples view over the built Iceberg table. DuckDB is linked into sal, so nothing has to be installed alongside it.

Terminal window
sal query

sal query opening a SQL shell with a triples view over the built table

The shell opens on the requested info query and leaves the triples view there to explore. Ctrl + R runs the editor’s statement, Ctrl + H lists the other keys, and Ctrl + D exits.

Info modes:

  • head: show the first rows from triples.
  • properties: show Iceberg table properties.
  • snapshots: show Iceberg snapshots and SAL Git tags.
  • column-stats: show Iceberg column statistics.

To compare snapshots:

Terminal window
sal query --snapshot-diff latest

Looks up the RDF resources inside a built data product. Where sal query is about the Iceberg table that stores the triples, sal get is about the RDF the table describes.

Lists every class the data product types a resource with, most instantiated first:

Terminal window
sal get classes
class instances
https://schema.org/Organization 75
http://www.w3.org/ns/dcat#Dataset 2
https://schema.org/AdministrativeArea 1

A class is the object of an rdf:type statement, and instances counts the distinct subjects declared to be of that class.

Lists every datatype the data product declares, with the annotations each one carries:

Terminal window
sal get datatypes
datatype label comment
https://w3id.org/sal/cgs-earth/sal-module-spec/salmodule#inbou... Inbound Listener Literal representing a [host:]<port>
https://w3id.org/sal/cgs-earth/sal-module-spec/salmodule#outbo... Outbound Endpoint Literal representing a URL or <host>:<port>

A datatype is a subject declared to be an rdfs:Datatype, so a data product that only holds instance data lists none. rdfs:label and rdfs:comment are optional; a column that no datatype states is left out of the table entirely.

Note that this lists the datatypes a data product defines, not the datatypes its literals are tagged with. The built triples table stores the lexical value of a literal rather than its datatype IRI, so ^^xsd:date and friends cannot be listed back out of it.

Lists every resource the data product instantiates, with the class it is typed with:

Terminal window
sal get instances
instance class
https://geoconnex.us/ref/dams/1078 http://www.w3.org/ns/dcat#Dataset
https://geoconnex.us/ref/dams/1079 http://www.w3.org/ns/dcat#Dataset
https://example.org/org/reclamation https://schema.org/Organization

Where sal get classes counts the instances of each class, this lists them. A resource typed with several classes is listed once per class.

The class is not required to be declared an rdfs:Class or an owl:Class in the data product, since a data product commonly types its resources with a vocabulary it does not itself carry. What is left out is the other direction: a subject that is itself an rdfs:Class, owl:Class, rdf:Property, owl:ObjectProperty, owl:DatatypeProperty, owl:AnnotationProperty, rdfs:Datatype, or owl:Ontology describes the schema rather than instantiating it, so it is not reported as an instance.

Shows every statement the data product makes about one subject:

Terminal window
sal describe https://github.com/cgs-earth/sal/Bob
predicate object
http://schema.org/jobTitle Professor
http://schema.org/name Jane Doe
http://schema.org/url http://www.janedoe.com
http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://schema.org/Person

This is the <subject> ?p ?o pattern, so it is a filter on the subject column rather than a SPARQL query.

A subject with no scheme is a term the project defined itself, so it is resolved against the project base the same way sal build resolves a relative term in the project’s RDF. In a checkout of https://github.com/cgs-earth/sal, the command above can be written as:

Terminal window
sal describe Bob

The project base comes from the git remote, so a relative subject only works inside a checkout that has one. A prefixed name such as schema:Bob is not expanded; a scheme is what makes a subject absolute, and schema: is one.

An IRI copied out of Turtle or a SPARQL pattern can be passed with the angle brackets it is written in:

Terminal window
sal describe '<https://geoconnex.us/ontologies/method/pastor>'

A subject with no statements is reported rather than printed as an empty table.

Serves the built triples table as a read-only SPARQL endpoint on port 8080:

Terminal window
sal serve

The endpoint implements the SPARQL Protocol query operation at /sparql. It accepts GET requests with a query parameter and POST requests using either application/sparql-query or application/x-www-form-urlencoded, and it answers with application/sparql-results+json.

Adds a web UI at / alongside the SPARQL endpoint:

Terminal window
sal serve --with-ui

The UI is a React app compiled into the sal binary, so it works offline and needs no extra install. It has five tabs:

  • Stats: triple, subject, predicate, and object counts, plus the Iceberg snapshots, table properties, and column statistics that sal query --info prints.
  • SQL: a DuckDB editor over the same triples view sal query opens, with sample queries and Ctrl/Cmd + Enter to run.
  • SPARQL: a YASGUI editor pointed at the local /sparql endpoint.
  • Modules: the browser form of sal salmodule inspect. Enter a module reference and the JSON-LD ontology it publishes is shown as formatted JSON. Every module the build downloaded is offered as a chip and as an autocompletion in the input, read from the sal.salmodules table property.
  • Map: reserved for rendering the object_geometry column. Not implemented yet.

Enabling the UI also exposes the JSON endpoints it reads:

  • POST /api/sql with a {"sql": "..."} body runs a DuckDB statement and returns its header and rows. Results are capped at 1000 rows, and the response message reports the full count when it truncates.
  • GET /api/stats returns the counts and Iceberg metadata shown on the Stats tab, including the modules list the Modules tab suggests from.
  • GET /geometries?limit=&offset= returns up to 100 object_geometry values at a time as GeoJSON.
  • GET /api/salmodule?module= clones, builds, and runs a SAL module, returning {"module": ..., "ontology": ...}. The first request for a module has to build its image, so it can take minutes.

/api/sql runs arbitrary DuckDB statements against your machine, and /api/salmodule builds and runs container images from repositories a caller names, so only enable --with-ui on a network you trust.

Publishes .sal/data as an OCI artifact:

Terminal window
sal push ghcr.io/my-org/my-data-product --username "$OCI_USERNAME" --password "$OCI_PASSWORD"

SAL uploads generated data files as OCI layers and annotates the artifact with the source Git remote and current commit hash. See Publishing Data.

Restores a published OCI artifact and its source repository:

Terminal window
sal clone ghcr.io/my-org/my-data-product:latest

SAL reads the artifact metadata, clones the recorded source repository, checks out the recorded commit, runs sal init, and restores the artifact layers into .sal/data.

Use --destination to choose the local clone path.

Pulls data from an OCI artifact. It uses the same artifact retrieval command shape as sal clone.

Uploads the built data product to an object store:

Terminal window
sal upload --bucket gs://my-bucket/sal/triples

SAL stages a temporary copy, rewrites staged Iceberg metadata to point at the target location, then uploads data and metadata in reader-friendly order. See Publishing Data.

Rewrites local Iceberg metadata so a copied table can be read from a new table root:

Terminal window
sal edit --new-table-root gs://my-bucket/sal/triples

This command changes metadata references only. It does not rewrite Parquet data files.

Removes or reshapes local build artifacts. Use --wipe to delete the local data product, or --squash to condense local snapshots ahead of a remote artifact.

Outputs SAL module information. The ontology subcommand prints SAL’s ontology information, and run runs a SAL project. See the SAL Module TTL Spec for the Turtle source.

The inspect subcommand prints the ontology that a remote module publishes:

Terminal window
sal salmodule inspect salmodule://github.com/adplincinst/sample-salmodule-1

SAL clones the module’s repository, builds the Dockerfile in its root, and runs the module’s ontology command, printing the JSON-LD it returns. Docker’s layer cache makes inspecting an unchanged module cheap after the first build.

The salmodule:// scheme is optional, so adplincinst/sample-salmodule-1 and the repository’s HTTPS URL both work. A host is only needed for modules that are not on GitHub.

The same thing is available in the browser from the Modules tab of sal serve --with-ui.

Reserved for tests on built SAL data products. This command is not yet implemented.