Your first environment
======================
In this tutorial you will build an OntoEnv environment from scratch, watch it
resolve an ``owl:imports`` chain, and export the result as a single file.
Everything runs locally except one optional step at the end.
You will need about 10 minutes.
.. raw:: html
The minimum you need to know
Three commands cover most day-to-day use:
.. code-block:: console
$ ontoenv init ./ontologies # build an environment from a directory
$ ontoenv list ontologies # see what it found
$ ontoenv closure out.ttl # export an ontology + all its imports
Everything below is that, slowed down.
Install the CLI
---------------
The command-line tool ships two ways. Pick whichever is easier for you:
.. code-block:: bash
# With a Rust toolchain:
cargo install --locked ontoenv-cli
# Or as part of the Python package (no Rust needed):
pip install ontoenv
Check that it worked:
.. code-block:: console
$ ontoenv version
ontoenv 0.6.0 @ 90f61b73604620bb5582b18e1d2d9dcd004b2fea
Create some ontologies
----------------------
Make a directory with two small ontology files. The first describes a
building; the second describes sensors and is imported by the first.
.. code-block:: bash
mkdir -p tutorial/ontologies
cd tutorial
Save this as ``ontologies/sensors.ttl``:
.. code-block:: turtle
@prefix owl: .
@prefix rdfs: .
@prefix sen: .
a owl:Ontology .
sen:Sensor a owl:Class ; rdfs:label "Sensor" .
sen:Thermometer a owl:Class ; rdfs:subClassOf sen:Sensor .
And this as ``ontologies/site.ttl``:
.. code-block:: turtle
@prefix owl: .
@prefix rdfs: .
@prefix sen: .
@prefix site: .
a owl:Ontology ;
owl:imports .
site:Room a owl:Class ; rdfs:label "Room" .
site:hasSensor a owl:ObjectProperty ;
rdfs:domain site:Room ;
rdfs:range sen:Sensor .
Note what ``site.ttl`` does *not* contain: any mention of where
``https://example.org/sensors`` lives. That is the problem OntoEnv exists to
solve. The import names an IRI, and something has to work out which file that
IRI corresponds to.
Initialize the environment
--------------------------
Run ``init`` and tell it which directory to scan:
.. code-block:: console
$ ontoenv init ./ontologies
Initialized environment with 2 unique ontologies (2 records).
Two things just happened. OntoEnv walked ``./ontologies``, parsed every RDF
file it found, and recorded the ontology IRI each file declares. It also
created a ``.ontoenv/`` directory to hold what it learned.
Every later command finds that directory by walking up from wherever you are,
so you can work from any subdirectory of ``tutorial/``.
See what was discovered
-----------------------
.. code-block:: console
$ ontoenv list ontologies
https://example.org/sensors
https://example.org/site
$ ontoenv list locations
file:///home/you/tutorial/ontologies/sensors.ttl
file:///home/you/tutorial/ontologies/site.ttl
OntoEnv now knows that the IRI ``https://example.org/sensors`` — the one
``site.ttl`` imports — comes from the file ``ontologies/sensors.ttl``. It made
that connection by reading the ``owl:Ontology`` declaration inside each file,
not by guessing from filenames. To see the pairing directly, use ``ontoenv
dump``.
For a summary of the environment itself:
.. code-block:: console
$ ontoenv status
Environment Path: /home/you/tutorial/.ontoenv
Number of Ontologies: 2
Last Updated: 2026-07-27 12:39:57 -06:00
Store Size: 6.00 KiB
Trace an import
---------------
Ask which ontologies depend on ``sensors``:
.. code-block:: console
$ ontoenv why https://example.org/sensors
Why https://example.org/sensors:
https://example.org/site -> https://example.org/sensors
``why`` prints every import path that reaches the given IRI, running from the
most distant importer down to the target. With one import there is only one
path; in a real project this is how you find out why some unexpected ontology
ended up in your environment.
Export a closure
----------------
Now for the payoff. Ask for ``site`` plus everything it transitively imports,
merged into a single file:
.. code-block:: console
$ ontoenv closure https://example.org/site closure.ttl
Open ``closure.ttl``. It contains the triples from *both* files. Two details
are worth noticing:
- The ``owl:imports`` statement is gone. It was resolved, so keeping it would
invite a consumer to try resolving it again.
- There is a single ``owl:Ontology`` declaration, for
``https://example.org/site``. The declarations from the imported graphs were
collapsed onto that root.
That is what OntoEnv means by a closure: not a raw concatenation, but a
flattened graph that stands on its own. If you want the raw merge instead,
:doc:`../explanation/views-and-copies` explains the difference and
``ontoenv union`` gives you it.
To get just one graph, without its imports:
.. code-block:: console
$ ontoenv get https://example.org/site
With no output file, ``get`` writes to standard output.
Add an ontology from the web
----------------------------
So far everything has been local. Add a published ontology and OntoEnv will
fetch it, then follow its imports and fetch those too:
.. code-block:: console
$ ontoenv add https://brickschema.org/schema/1.4.4/Brick.ttl
$ ontoenv list ontologies
You now have Brick and its dependencies alongside your own files. Remote
ontologies are cached on disk, so a second run does not re-download them —
:doc:`../how-to/work-offline` covers how long the cache is trusted and how to
work with no network at all.
Check for problems
------------------
.. code-block:: console
$ ontoenv doctor
No issues found.
``doctor`` looks for the mistakes that actually bite: two files declaring the
same ontology IRI, files with no ``owl:Ontology`` declaration at all, and
prefixes bound to conflicting namespaces.
.. code-block:: console
$ ontoenv list missing
This lists imports nothing in the environment can resolve — usually a typo, a
dead URL, or a file you have not added yet.
Clean up
--------
.. code-block:: console
$ ontoenv reset
This removes ``.ontoenv/`` and everything OntoEnv put in it. Your ontology
files are untouched.
What you learned
----------------
- An environment maps ontology IRIs to the places those ontologies live.
- ``init`` builds one from a directory; ``add`` registers individual files or
URLs and follows their imports.
- ``closure`` exports an ontology merged with its transitive imports, cleaned
up so the result stands alone.
- ``why``, ``doctor``, and ``list missing`` tell you what the import graph
looks like and where it is broken.
Next steps
----------
- :doc:`python` — the same workflow from Python, with SPARQL at the end.
- :doc:`../how-to/choose-what-gets-loaded` — glob and regex filters for when
scanning a whole directory pulls in too much.
- :doc:`../reference/cli` — every command and flag.