> For the complete documentation index, see [llms.txt](https://docs.controltheory.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.controltheory.com/controltheory-documentation/dstl8-docs/code-graph/configuration.md).

# Configuration

Declaring services with .dstl8.yaml, excluding files with .dstl8ignore, and language coverage

Two optional files at the repo root configure extraction: `.dstl8.yaml` declares the services a repo builds, and `.dstl8ignore` excludes paths. Neither is required — service detection and the built-in excludes cover the common cases.

## Declaring services: `.dstl8.yaml`

Services are how log sites map to what's actually running: a pattern link that says *service `checkout` emits this line from `payments/client.go:212`* is only as good as the repo→service mapping. The extractor detects services automatically, first match wins **per service name**, in this order:

| Precedence | Source               | Where it looks                                                                                               | `source` tag in the artifact |
| ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------- |
| 1          | `.dstl8.yaml`        | `services:` stanza at the repo root                                                                          | `dstl8_yaml`                 |
| 2          | Helm                 | `charts/**/values.yaml`                                                                                      | `helm`                       |
| 3          | Kubernetes manifests | `k8s/**/*.y{a,}ml`, `deploy/**/*.y{a,}ml` — `app.kubernetes.io/name` labels, `app:` labels, Deployment names | `k8s`                        |
| 4          | Compose / Procfile   | `docker-compose*.y{a,}ml` service keys, `Procfile` process names                                             | `compose`                    |
| 5          | Fallback             | the repo's short name as the single service (only when nothing else matched)                                 | `inferred`                   |

Every service in the artifact carries its `source` tag, so Möbius knows how much to trust the mapping. If detection gets it wrong — or a monorepo builds several services from different subtrees — declare them explicitly:

{% tabs %}
{% tab title="Single service" %}

```yaml
# .dstl8.yaml
services:
  - name: checkout
```

With no `paths`, the whole repo maps to the service. This is mainly useful when the deployed service name differs from the repo name and there are no manifests to detect it from.
{% endtab %}

{% tab title="Monorepo" %}

```yaml
# .dstl8.yaml
services:
  - name: checkout
    paths: ["cmd/checkout/**", "payments/**"]
  - name: checkout-worker
    paths: ["cmd/worker/**"]
  - name: admin-api
    paths: ["services/admin/**"]
```

`paths` are glob patterns relative to the repo root; a log site maps to the service whose globs cover its file.
{% endtab %}
{% endtabs %}

`name` is required for every entry. A malformed `.dstl8.yaml` is a hard error — it's the explicit override, so it fails loudly instead of silently falling back to detection. The `--service name=glob` CLI flag sits at the same trust tier: it wins over any detected or declared mapping with the same name, and other detected services are kept.

## Excluding files: `.dstl8ignore`

The extractor never reads a file you've told git to ignore, and `.dstl8ignore` excludes more on top. Exclusions are evaluated in this order:

1. **`.gitignore`** — always respected: files are enumerated with `git ls-files`, so **only git-tracked files are ever scanned**. Untracked and ignored files never enter the pipeline.
2. **`.dstl8ignore`** — extra excludes, at the repo root.
3. **Built-in excludes** (below).
4. **Test sources** (below) — unless you pass `--include-tests`.
5. **`--exclude` flags** on `build`/`push`.

`.dstl8ignore` uses gitignore pattern syntax:

* one pattern per line; blank lines are skipped
* `#` starts a comment line
* `!` re-includes something a previous pattern excluded — **the last matching rule wins**
* a trailing `/` matches directories only (and everything beneath them)
* `*`, `?`, and `[...]` match within one path segment; `**` crosses segments
* a pattern containing `/` is anchored to the repo root; a bare name matches at any depth

A worked example — keeping generated code out of the graph:

```gitignore
# .dstl8ignore — generated code carries no useful log sites
gen/**
api/*_generated.go
*.pb.ts

# drop a legacy tree wholesale, but keep the one file still in service
internal/legacy/
!internal/legacy/keep_this.go
```

Excluded files disappear from the artifact entirely — they're not in the file inventory, and their log sites are never extracted. Verify the effect with `dstl8 graph build --dry-run | jq '.files[].path'`.

{% hint style="info" %}
Outside a git repo (e.g. an exported source tarball), `git ls-files` isn't available and `.gitignore` is **not** parsed — the walk covers everything except `.git`. In that situation `.dstl8ignore` and `--exclude` are your only carve-outs.
{% endhint %}

### Built-in excludes

Applied after `.dstl8ignore`, always on:

* **Directories at any depth:** `vendor/`, `node_modules/`, `third_party/`, `dist/`, `build/`, `testdata/`
* **Generated/minified file suffixes:** `*.min.js`, `*_pb.go`, `*.gen.go`
* **Files larger than 1 MB** (counted in `stats.skipped_files`)
* **Binary files** (null byte in the first 8 KB)

There's no way to re-include a built-in exclude — vendored and generated code stays out by design.

### Test sources

Test files are excluded by default. A log statement inside a test never runs in production, so a runtime pattern that matches one is a false attribution — Dstl8 would place a live error in a fixture instead of in the code that actually emitted it.

Detection is by path, following each language's conventions:

|                             | Excluded                                                                                                                       |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **Directories** (any depth) | `test/`, `tests/`, `spec/`, `specs/`, `__tests__/`, `__mocks__/`, `test_suite/` — this also covers Maven/Gradle's `src/test/…` |
| **Go**                      | `*_test.go`                                                                                                                    |
| **Python**                  | `test_*.py`, `*_test.py`, `*_tests.py`                                                                                         |
| **JavaScript / TypeScript** | `*.test.js`, `*.spec.js` and the `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx` variants                                                |
| **Ruby**                    | `*_spec.rb`, `*_test.rb`                                                                                                       |
| **Java / Kotlin**           | `*Test.java`, `*Tests.java`, `*Test.kt`, `*Tests.kt`                                                                           |
| **C#**                      | `*Test.cs`, `*Tests.cs`                                                                                                        |
| **PHP**                     | `*Test.php`, `*Tests.php`                                                                                                      |
| **Rust**                    | `*_test.rs`, plus the crate's `tests/` directory                                                                               |
| **Elixir**                  | `*_test.ex`, `*_test.exs`                                                                                                      |

Files that merely look like tests are kept — `internal/testing/harness.go`, `pkg/attestation/verify.go` and `services/latest/handler.go` all stay in the graph.

Tests inside a production file (Rust's `#[cfg(test)] mod tests`, a nested JUnit class) can't be detected by path and are still indexed. If those produce noisy matches, exclude the file with `.dstl8ignore`.

`dstl8 graph build` reports the count so the effect is never invisible:

```
Files:       269 (1 skipped)
Tests:       60 excluded (--include-tests to index them)
```

Pass `--include-tests` on `build`/`push` to index them anyway — useful if your tests emit the same log statements you're trying to trace, or if you want deploy diffs to include test-only changes.

## Language coverage

Every text file that survives the walk appears in the file inventory (path, language, line count, content hash). What else is extracted depends on the language:

| Languages                                                              | Parser                     | Log sites                               | Symbols & imports                                                                           |
| ---------------------------------------------------------------------- | -------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------- |
| Go                                                                     | Go compiler AST            | Yes (`confidence: extracted`)           | Yes — functions, methods, types; import edges; `go.mod` deps                                |
| Python, JavaScript, TypeScript, TSX, Java, Ruby, C#, Rust, Kotlin, PHP | tree-sitter grammars       | Yes (`confidence: extracted`)           | Yes — symbols where the grammar supports definition tagging; per-language import extraction |
| Everything else with a recognizable syntax (\~250 languages)           | lexer token-scan heuristic | Yes, heuristic (`confidence: inferred`) | No                                                                                          |
| Shell, YAML, JSON, Markdown                                            | none (inventory-only)      | No                                      | No                                                                                          |

Notes on coverage:

* **Log-site fidelity:** full parsers find log calls from the syntax tree — logger calls (`log.Error`, `logger.warning`, `console.error`, `tracing::error!`, …), error constructors (`fmt.Errorf`, `errors.New`), and any call whose message argument is a string literal of three or more words. Format-string holes (`%s`, `{}`, `${…}`, f-string expressions) become wildcards in the normalized template.
* **Non-literal messages** (a message built entirely at runtime) keep the site — file, line, function, severity — with an empty template and `confidence: ambiguous`. Still useful: "this function logs at error level."
* **Heuristic languages** get log sites only, tagged `confidence: inferred`, from a lexer token scan (logger-looking callee, open paren, string literal). No symbols, no import edges.
* **Language detection** is by file extension first, then well-known basenames (`Rakefile`, `Gemfile`), then the shebang line (`#!/usr/bin/env python3` and friends). Files with an unknown extension still get the heuristic scan.
* **External dependencies** come from manifests, not source: `go.mod`, `package.json`, `requirements.txt`, `pyproject.toml`, `Gemfile`, `Cargo.toml`, `pom.xml`, `build.gradle`/`build.gradle.kts`, and `*.csproj`.

`--no-symbols` turns off the symbols/imports/external-deps layer entirely, for teams that want the artifact limited to the file inventory and log sites — see the [CLI reference](/controltheory-documentation/dstl8-docs/code-graph/cli.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.controltheory.com/controltheory-documentation/dstl8-docs/code-graph/configuration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
