STXT Discovery
1. Introduction
This document is STXT-DISCOVERY-SPEC; the other specifications cite it by that name.
This document defines STXT Discovery: the mechanism by which a tool locates,
on the file system, the @stxt.schema and @stxt.template documents applicable to
an STXT document.
The STXT-SCHEMA-SPEC and STXT-TEMPLATE-SPEC specifications define how definitions are written and how they validate; they deliberately do not state where they are located. This specification covers that omission with a single goal: that the same document validates against the same definitions in every tool, whether an editor, a command line or a continuous integration process. If every tool searched for definitions its own way, the outcome of validating would depend on who validates.
Resolution is identical for schemas and templates: both are discovered by the same procedure and compete for the same namespaces.
1.1 Date and status of this specification
This specification carries its own date and its own status in the Last modif and
Status fields of its Metadata, independent of the other STXT specifications' and with
the meaning fixed by STXT-SPEC §1.1. It is in Aurora.
It depends on STXT-SPEC and STXT-SCHEMA-SPEC.
2. Terminology
The keywords "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" must be interpreted according to RFC 2119 and RFC 8174: they carry that meaning only when they appear in uppercase, as here.
Terms such as node, namespace, schema and template retain their meaning in STXT-SPEC, STXT-SCHEMA-SPEC and STXT-TEMPLATE-SPEC.
Additional definitions:
- Definition: a
@stxt.schemaor@stxt.templatedocument, associated with a target namespace. - Resolution directory: a directory named
.stxtwhose contents are definitions. - Level: each of the resolution directories applicable to a document, ordered by precedence.
- Resolution chain: the ordered list of levels applicable to a specific document.
3. The .stxt directory
A resolution directory is a directory named exactly .stxt.
- A tool MUST load all files with the
.stxtextension under the directory, recursively. - Subdirectories carry no meaning: they are organization only.
.stxt/web/a.stxtand.stxt/a.stxtbelong to the same level. - Every file under a resolution directory MUST be a definition: a document whose
root node belongs to
@stxt.schemaor@stxt.template. Any other content is a resolution error (section 8). - The recursive walk SHOULD be guarded against pathological structures the same way
the ascent is (section 4.1): a tool MAY limit the descent depth and
SHOULD NOT follow symbolic links: neither descend through a link to a directory (so
as not to enter loops or walk unrelated trees) nor load as a definition a link to a file
(so as not to read files from outside the
.stxt/, section 10). A subdirectory that cannot be listed contributes no files; it does not stop the resolution of the rest of the level.
4. The resolution chain
For a given document, the resolution chain is built in this order, from highest to lowest precedence:
- Project level: the
.stxtdirectories found by walking up from the document's directory to the root of the file system, from nearest to farthest. - User level: the
.stxtdirectory in the user's home folder. - System level: the machine-wide definitions directory.
4.1 Project level
Starting from the directory containing the document, the tool MUST examine that
directory and each of its ancestors, in ascending order, and add to the chain
every .stxt directory that exists.
- The search MUST NOT stop at the first directory found: in a monorepo, the
subproject's
.stxtand the repository root's.stxtboth take part, and the subproject's one has higher precedence for being nearer. - The search ends at the root of the file system. A tool MAY impose an ascent limit (for example, 32 levels) as a safeguard against pathological paths (section 10).
- The
.stxtentry of an ancestor MUST be a real directory: if it is a symbolic link, the tool MUST NOT follow it and that ancestor contributes no level, even when the link points to a directory. The ancestors of a document are written by whoever created the project, who is not always the validating party (a cloned repository) and a.stxtlinked to the home folder or to the root would take the resolution into a foreign tree (section 10). It is the same rule that applies inside the directory (section 3). The user and system levels (section 4.2) and theSTXT_PATHentries (section 6), chosen by the user, may be links. - A document with no location on the file system (standard input, an unsaved buffer) has no project level: its chain starts at the user level.
4.2 User level and system level
| Level | Linux, macOS and other Unix | Windows |
|---|---|---|
| User | $HOME/.stxt |
%USERPROFILE%\.stxt |
| System | /etc/stxt |
%ProgramData%\stxt |
- The user level allows personal definitions shared across projects.
- The system level allows an organization to distribute definitions to every account on a machine.
- If any of the directories does not exist, that level contributes no definitions; it is not an error.
- Both directories are chosen by the user or by the machine's administrator, not by
the author of a project. They MAY therefore be symbolic links and a tool MUST
follow them:
$HOME/.stxtlinked to a personal configuration repository is an intended use. A linked$HOME/.stxtdoes not take part as a project level of a document located under the home folder (section 4.1), but it does as the user level. Inside them section 3 applies: the links they contain are not followed.
5. Precedence
Once every level of the chain is loaded, precedence applies per target namespace, not per whole directory:
- For each namespace, the active definition is the one from the nearest level that defines it. Definitions of the same namespace at farther levels are ignored.
- Different namespaces MAY resolve from different levels: the project's template
from its
.stxt, and a personal definition from$HOME/.stxt, in the same validation. - Within a single level, two definitions for the same namespace are an error (section 8), whether they are two schemas, two templates, or a schema and a template. Across different levels there is no conflict: the nearest one wins, whatever its kind.
For the scope of this specification, this rule pins down the schema-versus-template prioritization criterion that STXT-TEMPLATE-SPEC leaves open to the implementation: the effective semantic source of a namespace is its active definition.
5.1 Complete example
/home/ana/
├── .stxt/ (user level)
│ └── notes.stxt defines org.ana.notes
└── projects/monorepo/
├── .stxt/ (project level, 2nd)
│ ├── common.stxt defines com.acme.common
│ └── web-old.stxt defines com.acme.web
└── web/
├── .stxt/ (project level, 1st)
│ └── web.stxt defines com.acme.web
└── index.stxt document to validate
The resolution chain of index.stxt is, in order:
web/.stxt → monorepo/.stxt → /home/ana/.stxt → /etc/stxt.
Result per namespace:
com.acme.web→web/.stxt/web.stxt(the nearest level wins;web-old.stxtis ignored).com.acme.common→monorepo/.stxt/common.stxt.org.ana.notes→/home/ana/.stxt/notes.stxt.
6. The STXT_PATH environment variable
If the STXT_PATH environment variable is defined, it completely replaces the
resolution chain of section 4: no project, user or system level is searched.
- Its value is a list of directories separated by the platform's path separator
(
:on Unix,;on Windows). - Each directory in the list is one level; the order of the list is the order of precedence (the first one has the highest priority).
- Entries point directly to definition directories: they do not need to be
named
.stxt. - A nonexistent entry is ignored; it is not an error.
- An entry MAY be a symbolic link, and it is followed: whoever defines
STXT_PATHchooses its directories (section 10). Inside it section 3 applies. - An empty entry (the one left by a leading, trailing or doubled separator, as in
:/opt/defs) is ignored as well: it does not stand for the working directory. - An
STXT_PATHthat is defined but empty leaves the chain empty: no namespace has an active definition, and validating a document with a namespace fails withSCHEMA_NOT_FOUND(STXT-SCHEMA-SPEC §13). An empty chain is not a "syntax only" mode: that mode, when a tool offers it, is requested explicitly.
STXT_PATH exists for environments in which the implicit search is not appropriate:
continuous integration, the tools' own tests, or environments with a restricted
file system.
7. Per-document resolution
The resolution chain is defined per document: it is a function of the document's location and the environment, not of the tool or of the set of documents being processed.
A tool that processes several documents at once (an editor with several open files, a command line with several arguments) MAY share loads and caches as an optimization, but the result MUST be identical to resolving each document separately. In particular, if two documents from different projects see different definitions for the same namespace, each one MUST be validated with its own.
A tool MAY offer explicit mechanisms to designate definitions (a command-line option, editor configuration). What is designated explicitly MUST take precedence over what this specification discovers.
8. Resolution errors
A tool MUST report as a resolution error:
- Two definitions for the same target namespace at the same level.
- A file under a resolution directory that does not parse as STXT.
- A file whose root node belongs neither to
@stxt.schemanor to@stxt.template. - A definition that does not validate against its meta-schema (STXT-SCHEMA-SPEC, STXT-TEMPLATE-SPEC).
Rules:
- Upon a resolution error, the tool MAY continue loading the remaining definitions, but MUST report the error.
- Upon error 1, the tool MUST NOT silently pick one of the conflicting definitions: the affected namespace has no active definition while the conflict exists.
- A document declaring a namespace for which the chain contributes no definition
is not a resolution error: the document parses, and validating it produces
SCHEMA_NOT_FOUND(section 6, STXT-SCHEMA-SPEC §13). A tool SHOULD distinguish that case from a namespace with resolution errors.
9. Conformance
- Definition discovery is part of the schema/template layer, optional like the layer itself (STXT-SPEC §17.3): a conforming core parser is not required to implement it.
- A tool that implements file-system discovery MUST follow this specification in full: implementing only part of the chain produces exactly the disagreement between tools that this specification exists to avoid.
- This specification does not define when definitions are reloaded (file watching, caches, invalidation): that is each tool's decision, as long as the observable result honors sections 4 to 8.
10. Security Considerations
- The upward search and the user and system levels load files from directories the
validating party does not necessarily control (the ancestors of a shared path,
/etc/stxt). A definition only validates: it does not execute code or alter the document. The impact of a hostile definition is limited to changing the validation outcome. - Even so, a tool SHOULD be able to show which directories it loaded and where the active definition of each namespace came from, and MAY offer trust restrictions analogous to those of other tools with upward searches (for example, limiting the search to a configured root directory).
- Both the ascent limit of section 4.1 and the limit on the recursive descent
within a resolution directory (section 3) protect against pathological
directory structures: circular symbolic links or virtual file systems. A tool
SHOULD bound the descent depth and SHOULD NOT follow symbolic links when
descending, neither directory nor file links, so that a resolution directory loads only
the definitions it really contains, not files an outside link points to: since the levels
of the chain include directories the validating party does not control, following a file
link would allow reading a file from outside the
.stxt/(and leaking its content through a resolution error). A directory that cannot be listed contributes no definitions and does not stop the resolution of the rest. - For the same reason, the
.stxtof an ancestor that is a symbolic link forms no level (section 4.1): the ancestors are the part of the chain written by whoever created the project, not by the validating party, and a.stxtlinked to the home folder or to the root in a cloned repository would be enough for the tool to walk that whole tree, parsing every file as a definition and leaking the line that fails to parse. The user and system levels and theSTXT_PATHentries are followed when they are links (section 4.2, section 6): the user chooses them, and linking$HOME/.stxtto a personal configuration repository is an intended use.