> For the complete documentation index, see [llms.txt](https://ascribed-dev.com/docs/llms.txt). This documentation describes Ascribe 0.4.4. Anything marked in main, not yet released isn't in that release yet.

# `ascribe.toml` format contract: what pages draw from

> Reference · The normative contract for the content model's sources, with ascribe.lock, and APIs.

Part of the [`ascribe.toml` format contract](https://ascribed-dev.com/docs/contracts/content-model.md), whose [contents](https://ascribed-dev.com/docs/contracts/content-model.md#contents) list every section. What these keys are for, with examples, is in the reference's [what pages draw from](https://ascribed-dev.com/docs/content-model/sources.md).

## 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](https://ascribed-dev.com/docs/contracts/tested-examples.md#1-where-they-are)). Both are written by `ascribe sources fetch` and `ascribe sources update`, and committed. `check` and `build` read only the copies.

```toml
# 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](https://ascribed-dev.com/docs/checks/diagnostics.md).

---

## 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](https://ascribed-dev.com/docs/glossary.md#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.

```toml
[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.
