Skip to content

Extends (Presets)

The extends field lets you inherit configuration from other files or remote repositories. This enables sharing common rulesets across projects and teams.

runok supports three ways to specify an extends source:

Reference a configuration file on the local filesystem.

runok.yml
extends:
- ./shared/base.yml
- ~/company/runok-base.yml

Path resolution rules:

  • ~/... expands to $HOME
  • ./... and relative paths resolve from the directory containing the current config file
  • Absolute paths are used as-is
  • Path traversal beyond the filesystem root is rejected

Reference a configuration file in a GitHub repository using the github: prefix.

runok.yml
extends:
- github:example-org/[email protected]
- github:example-org/security-rules@main
- github:example-org/security-rules # uses default branch
- github:example-org/runok-presets/readonly-unix@v1 # specific file in repo

Format: github:<owner>/<repo>[/<path>][@<ref>]

The optional /<path> specifies a preset file within the repository (without .yml/.yaml extension). When omitted, runok reads runok.yml (or runok.yaml) from the repository root. When provided, runok reads <path>.yml (or <path>.yaml).

The @<ref> part is optional and can be:

  • A tag (e.g., v1.0.0)
  • A branch name (e.g., main)
  • A full commit SHA (40-character hex string)
  • Omitted to use the repository’s default branch

Reference any Git repository by URL.

runok.yml
extends:
- https://github.com/example-org/runok-config.git
- https://github.com/example-org/[email protected]
- [email protected]:example-org/runok-config.git@main

Supports https://, http://, and git@ URLs. An optional @<ref> suffix specifies the version.

runok distinguishes between mutable and immutable references for caching:

Reference TypeExampleCaching Behavior
Commit SHA (40 hex)github:org/repo@a1b2c3d4...Cached permanently
Tag or branchgithub:org/[email protected]Cached with TTL
Default branchgithub:org/repoCached with TTL

Mutable references (tags, branches) are cached for 24 hours by default. Immutable references (commit SHAs) are cached permanently. See Environment Variables for how to configure the cache TTL.

When a config file specifies extends, runok resolves presets using depth-first traversal:

  1. For each entry in extends (in order), recursively load and resolve its own extends.
  2. Merge all resolved presets in order.
  3. Merge the current config on top.

This means the current file always takes the highest priority, and earlier extends entries serve as the base.

Diamond-shaped extends (where two presets share a common ancestor) are allowed. The shared ancestor is loaded once for each path, and both copies are merged.

a.yml
# a.yml
extends:
- ./b.yml
- ./c.yml
# b.yml extends: [./shared.yml]
# c.yml extends: [./shared.yml]
# This is valid — shared.yml is merged from both paths.

Circular references are detected and rejected. runok normalizes file paths before checking, so ./runok.yml and runok.yml pointing to the same file are correctly identified as circular.

The maximum extends depth is 10 levels. Exceeding this limit produces an error.

Remote presets are cached locally to avoid repeated network fetches.

The cache directory is determined by:

  1. $XDG_CACHE_HOME/runok/presets (if XDG_CACHE_HOME is set)
  2. $HOME/.cache/runok/presets (fallback)

Each cached preset is stored in a directory named by the SHA-256 hash of the reference string.

ScenarioAction
Cache hit (fresh)Use cached data directly.
Cache staleAttempt to fetch updates. If fetch fails, use stale cache.
Cache missClone the repository. Fail if clone fails.

Stale cache provides resilience against temporary network failures — if a fetch fails, the previously cached version is used with a warning.

Each cached entry includes a metadata.json file tracking:

  • fetched_at — Unix timestamp of when the preset was fetched
  • is_immutable — Whether the reference is a commit SHA
  • reference — The original reference string
  • resolved_sha — The resolved commit SHA (if available)