ascribe.toml format contract: what pages draw from
The normative contract for the content model’s sources, with ascribe.lock, and APIs.
For AI agents: the documentation index is at llms.txt, and this page is available as Markdown.
Part of the ascribe.toml format contract, whose contents list every section. What these keys are for, with examples, is in the reference’s what pages draw from.
20. [sources.<name>]
A source is a folder outside the content root, or another repository, that a page’s snippets MAY read (SPEC §4.8, §7.3, §7.4); a page MUST NOT read a file outside the project’s folder any other way. <name> is the source’s name (§1.2), and a snippet’s address starts with it.
| Key | Type | Default | Description |
|---|---|---|---|
path |
string (path) | required without git |
The source’s folder, relative to the project root. It must exist, be a directory, and be inside the git repository the project is in, when the project is in one. |
git |
string (URL) | required without path |
Another repository, as git takes it: https://, http://, ssh://, git://, file://, or user@host:path. |
branch |
string | the repository’s default branch | The branch ascribe sources update moves the pin to. |
include |
array of patterns | every file | The files a snippet may read, matched against their paths relative to the source’s folder (§1.3). |
ignore |
array of patterns | node_modules and .git folders, always |
Files left out even when include matches them. Every node_modules and .git folder is left out whatever the list says, before the patterns it adds. |
Rules (§25.8): a source MUST have exactly one of path and git, branch only with git, a URL git can be given, and a branch name git accepts (model-source-remote). path MUST be relative (model-path-absolute) and name a directory that exists (model-source-path-missing), inside the git repository the project is in, when it’s in one (model-source-outside-repository). With no repository, the language server and ascribe mcp MUST read a source only inside what the user opened, a folder of the workspace or the folder ascribe mcp was started in, and report one outside it as model-source-outside-repository with the variant opened; a command reads a source wherever path leads. With git, sources/ MUST be outside the content root (model-source-remote).
20.1 ascribe.lock and the copies
A source with git is pinned to a commit in ascribe.lock, beside ascribe.toml, and the files its snippets use are copied into sources/<name>/ at the paths they have in their repository. So are the repository’s ascribe.examples.toml and every file in its examples.observations/, when it has them, whatever the source’s include says (tested examples). Both are written by ascribe sources fetch and ascribe sources update, and committed. check and build read only the copies.
# Written by `ascribe sources fetch` and `ascribe sources update`. Don't edit it by hand.
version = 1
[[source]]
name = "api"
git = "https://github.com/acme/api.git"
commit = "9f2c41d0e0c4a1b2c3d4e5f60718293a4b5c6d7e"
missing = ["examples/quickstart.ts"]
[source.files]
"src/auth.rs" = "sha256:5d41402abc4b2a76b9719d911017c592ae2fd2b1f6b3b0f0d5ce64b4c7d0e1a2"
| Key | Type | Description |
|---|---|---|
version |
integer | The lock’s format: 1. |
source[].name |
string | The source’s name. Each source is pinned once. |
source[].git |
string | The source’s git when it was pinned. |
source[].commit |
string | The commit, in full: 40 or 64 hexadecimal digits. |
source[].files |
table | Each copy’s path, relative to the source’s folder, and the SHA-256 of its bytes: sha256: and 64 lowercase hexadecimal digits. |
source[].missing |
array of strings | Optional. The paths, relative to the source’s folder, that snippets named and that weren’t in the repository at the commit, so weren’t copied. No path is in both files and missing. |
check reads the lock and the copies with the source files, and reports what doesn’t match: a lock it can’t read (lock-invalid); a pin of a source ascribe.toml doesn’t declare with git, or declares with another git (lock-source-unknown); a copy that’s missing or doesn’t have its hash (source-copy-changed); a file in sources/<name>/ the lock doesn’t list (source-copy-unlocked); and a copy no snippet uses (source-copy-unused, a warning), which a tested examples’ config or observation never is. A snippet that names a file with no copy is snippet-file-missing: its message says to run ascribe sources fetch, or, for a file the lock lists in missing, that the file isn’t in the repository at the pinned commit, which fetch can’t change. These are source-file diagnostics; see the diagnostics reference.
21. [apis.<name>]
OpenAPI specs that pages link into, by name: each one’s file, and where its reference is published. A link to a declared spec with a fragment, [Get a user](../api/openapi.yaml#getUser), is a link to the operation whose operationId is the fragment, percent-decoded (SPEC §7.11); it’s checked against the spec, and written as the operation’s address in the reference.
[apis.public]
spec = "api/openapi.yaml"
reference = { renderer = "redoc", at = "/api/" }
[apis.partner]
spec = "../services/partner/openapi.json"
reference = "/partner-api/#/operations/{operationId}"
| Key | Type | Default | Description |
|---|---|---|---|
spec |
string (path) | required | The spec’s file, YAML or JSON, relative to the project root. It may be outside the project’s folder, inside the git repository the project is in, when it’s in one. |
reference |
table or string | required | Where the published reference is: { renderer = "redoc", at = "/api/" } for Redoc’s page, or an address pattern, an operation’s address with {operationId} and {tag}, for another renderer. |
spec is read as YAML, or as JSON when its name ends in .json, the same way for Swagger 2.0 and OpenAPI 3.0 to 3.2: each operation of paths, with its method, path, operationId, summary, first tag, and deprecated. Nothing else is read, and nothing is validated. A path item that’s a $ref to another file isn’t followed. A spec outside the project’s folder is read for its operations, and a link to it with no fragment is still a link outside the project (link-spec-outside-project).
reference is a table for Redoc:
| Key | Type | Default | Description |
|---|---|---|---|
renderer |
string | required | The renderer that publishes the reference: "redoc". |
at |
string | required | The address of Redoc’s page, relative to [consumer] base-path, or absolute when the reference is published elsewhere. |
Redoc’s address of an operation is <at>#operation/<operationId>, which Redoc 2 and 3 both open. Any other renderer’s is an address pattern: a string with {operationId}, and {tag} for the operation’s first tag when the renderer’s address has it, each filled in percent-encoded. Swagger UI’s is "/api/#/{tag}/{operationId}", and Stoplight Elements’ with its hash router "/api/#/operations/{operationId}". Either address is joined to [consumer] base-path, unless it’s absolute (https://… or //…), for a reference published on another site. It’s the site’s base path in every line of a versioned build: an API’s reference isn’t a line’s.
Rules (§25.13): spec MUST be relative (model-path-absolute) and, when the project is in a git repository, inside it (model-api-outside-repository); with no repository, the language server and ascribe mcp MUST read a spec only inside what the user opened, and report one outside it as model-api-outside-repository with the variant opened, as a source’s folder is (§18). reference MUST be a table with exactly renderer and at, renderer one of the choices (model-missing-key, model-unknown-key, model-invalid-value), or a string, an address pattern, with {operationId} and no placeholder but {operationId} and {tag} (model-api-reference). What needs the spec’s file is checked with the project, not by the loader: SPEC §8.2’s api-spec-unreadable, api-spec-empty, link-operation-missing, and link-spec-outside-project. A compiler MUST percent-encode each value an address pattern’s placeholder takes, keeping only RFC 3986’s unreserved characters, and MUST join an address that isn’t absolute to the site’s [consumer] base-path, without a line’s folder.