Skip to main content
A Colin document is a Markdown file that can contain Jinja templating. Documents form the building blocks of your context graph, and each one compiles to a standalone output that can reference and incorporate content from other documents. Documents live in your project’s models/ directory (or whatever you configure as model-path in colin.toml). When you run colin run, Colin discovers all .md files in this directory and compiles them based on their dependencies.

File Structure

Every document consists of two parts: optional YAML frontmatter followed by the document body.
models/company.md
---
name: Company Overview
description: Core information about our company
---

Acme Corp builds developer tools that streamline CI/CD pipelines.
Founded in 2020, we serve over 500 enterprise customers.
The frontmatter defines metadata about the document. The body contains Markdown content, optionally enhanced with Jinja templating for dynamic behavior. See Templating for details on expressions, filters, LLM blocks, and other template features.

Frontmatter

Frontmatter is optional. When present, it’s YAML wrapped in --- markers at the top of the file:
---
name: Product Brief
description: Summary of product capabilities for sales team
---
Two fields are particularly useful: name - A human-readable identifier for the document. When omitted, Colin derives the name from the filename. description - Explains what this document contains. This metadata travels with the document when referenced, helping LLMs understand context. Documents without frontmatter work fine—Colin infers the name from the filename and leaves other metadata empty. This is useful for simple content files or when you’re referencing non-Markdown sources. Project-level configuration (paths, MCP servers) belongs in colin.toml. See Configuration for details.

Document Settings

The colin: block in frontmatter controls document-specific compilation behavior:
---
name: Market Analysis
colin:
  output:
    format: json
    path: reports/market.json
    publish: true
  cache:
    policy: auto
    expires: 1M
---

Output Configuration

The output block controls how documents are transformed and where they’re written:
colin:
  output:
    format: json              # transformation (default: markdown)
    path: reports/daily.json  # artifact location (optional)
    publish: true             # copy to output/ (optional)
format — The renderer to use for transforming content:
FormatOutputDescription
markdown.mdDefault passthrough (no transformation)
json.jsonConvert markdown structure to JSON
yaml.yamlConvert markdown structure to YAML
See Renderers for details on each format. path — Custom output location relative to output/. Supports subdirectories:
colin:
  output:
    format: json
    path: config/database.json  # writes to output/config/database.json
If omitted, Colin uses the source filename with the format’s extension (e.g., models/report.mdoutput/report.json). publish — Whether to copy the compiled artifact to output/. Defaults to true. Set false to keep the file in .colin/compiled/ only—useful for helper templates that other documents reference but shouldn’t appear in final output.

Private Files

Files can be excluded from output/ in two ways: Naming convention: Prefix the filename or any parent directory with _. These files compile normally but don’t publish:
models/_helpers/formatting.md    → private (underscore directory)
models/_config.md                → private (underscore file)
models/public.md                 → published
Explicit config: Set publish: false to make any file private, or publish: true to override the underscore convention:
colin:
  output:
    publish: false  # keep out of output/ regardless of name
Private files are fully accessible via ref().content—you can include their content in other documents. However, ref().path raises an error because linking to files that won’t exist in output/ is a bug.

Cache Policy

The cache policy controls when a document gets recompiled:
PolicyBehaviorRebuilds when
autoSmart caching (default)Source or refs change, or time expires
alwaysAggressive cachingSource changes, time expires, or --no-cache
neverNo cachingEvery run
colin:
  cache: never  # Always rebuild
Use never for documents that incorporate live data or timestamps. Use always for expensive operations where the output doesn’t change. For shorthand, you can specify just the policy value:
colin:
  cache: always

Expiration Threshold

The expires setting defines when a document becomes stale based on time elapsed since last compilation:
colin:
  cache:
    expires: 1d  # Rebuild after 1 day
Supported duration formats:
FormatMeaningExample
NmN minutes30m
NhN hours24h
NdN days7d
NwN weeks2w
NMN calendar months1M, 3M
NQN calendar quarters1Q
Months and quarters use calendar-aware arithmetic. For example, 1M after January 31st is February 28th (or 29th), not “30 days later.”

Calendar-Aligned Expiration

For expiration based on calendar boundaries rather than elapsed time, use the c prefix:
colin:
  cache:
    expires: 1cM  # Rebuild when calendar month changes
FormatMeaningBoundaryValid N
NcmEvery N minutes:00, :15, :30, :45 etc.Divides 60
NchEvery N hours00:00, 06:00, 12:00 etc.Divides 24
1cdNew calendar dayMidnight1 only
1cwNew calendar weekMonday midnight (ISO)1 only
NcMEvery N months1st of monthDivides 12
NcQEvery N quartersStart of quarterDivides 4
Calendar-aligned values must divide evenly into their containing period. For example, 30cm creates boundaries at :00 and :30, while 15cm creates boundaries at :00, :15, :30, and :45. Use 2cM for bimonthly updates or 2cQ for semiannual updates. A document compiled on January 31st with expires: 1cM expires on February 1st at midnight—when the calendar month changes—regardless of how much time has passed. With expires: 1cQ, a document compiled any time in Q1 (Jan-Mar) expires at the start of April 1st.

Cache Invalidation

A document is considered stale and recompiles if any of these conditions are true:
  • The source file changed
  • Any referenced document was updated more recently
  • The expiration threshold (if set) has been exceeded
When a document recompiles, all documents that reference it also recompile to pick up the changes.

Document URIs

Every document has a URI derived from its path relative to models/, without the .md extension:
FileURI
models/company.mdcompany
models/products/main.mdproducts/main
models/team/engineering/overview.mdteam/engineering/overview
Reference documents using their URI with ref():
{{ ref('company').content }}
{{ ref('products/main').content }}
Colin validates that referenced documents exist at compile time. See References for details on the ref() function and dependency tracking.