Configuration
TLDR: tix.toml maps file paths to contexts (like @nixos or @home-manager), controlling which stubs get loaded and how module parameters are typed.
tix.toml
Tix auto-discovers tix.toml by walking up from the file being checked. You can also pass --config path/to/tix.toml explicitly.
Contexts
A context tells tix “files matching these paths are NixOS modules (or Home Manager modules, etc.)” so it knows how to type the standard { config, lib, pkgs, ... }: parameter pattern.
[context.nixos]
includes = ["modules/*.nix", "hosts/**/*.nix"]
stubs = ["@nixos"]
[context.home-manager]
includes = ["home/*.nix"]
stubs = ["@home-manager"]
- includes — glob patterns matching files in this context
- excludes — glob patterns for files to exclude even when
includesmatches. Useful when a broad glob likedir/**/*.nixcovers a directory with a few files that belong to a different context. - stubs — which stub sets to load.
@nixosand@home-managerare built-in references to the generated NixOS/Home Manager stubs (requires[stubs.generate]or theTIX_BUILTIN_STUBSenv var)
For example, if most files under common/ are NixOS modules but common/homemanager/ contains Home Manager modules:
[context.nixos]
includes = ["common/**/*.nix", "hosts/**/*.nix"]
excludes = ["common/homemanager/**/*.nix"]
stubs = ["@nixos"]
[context.home-manager]
includes = ["common/homemanager/**/*.nix"]
stubs = ["@home-manager"]
tix init generates excludes patterns automatically when it detects mixed-kind directories.
What contexts do
When a file matches a context, tix automatically types the module’s function parameters. A NixOS module like:
{ config, lib, pkgs, ... }:
{
services.foo.enable = true;
}
Gets config, lib, and pkgs typed according to the context’s stubs, without any doc comment annotations in the file.
callPackage / dependency-injected files
For files loaded via callPackage or import that take a package set as their parameter:
[context.callpackage]
includes = ["pkgs/**/*.nix"]
stubs = ["@callpackage"]
@callpackage derives its types from the built-in Pkgs module (the same one that types pkgs.stdenv.mkDerivation, pkgs.fetchurl, etc.). Parameters not covered by the built-in stubs remain untyped. For broader coverage, generate pkgs stubs and load them via --stubs or the stubs config key — they merge into the Pkgs type alias automatically.
Custom context stubs
For module systems other than NixOS/Home Manager (e.g. flake-parts, devenv),
point stubs at a local .tix file:
[context.flake-parts]
includes = ["modules/**/*.nix"]
stubs = ["./flake-parts.tix"]
The file can contain top-level val declarations and/or module blocks;
both contribute to context args. See Custom context stubs from a file for the full pattern.
Inline context annotation
You can also set context per-file with a doc comment at the top:
/** context: nixos */
{ config, lib, pkgs, ... }:
{
# ...
}
Project settings
The [project] section configures project-level behavior for both the LSP and tix check.
[project]
includes = ["lib/*.nix", "pkgs/**/*.nix"]
excludes = ["result", ".direnv", "vendor/**"]
- includes — glob patterns for files to include in analysis. When the LSP starts, these files are analyzed in the background and their inferred types become ephemeral stubs available to all open files.
- excludes — glob patterns for files/directories to skip during
tix check. Excluded files are fully skipped from discovery. Hardcoded ignores (.git,node_modules,result,.direnv,target) are always applied.
tix init generates a [project] section with sensible defaults.
Files matching includes are processed in the background after LSP initialization. As each file’s type is inferred, any open files that import it are automatically re-analyzed with the updated type information.
Suppression directives
Tix supports TypeScript-style comment directives for suppressing diagnostics:
# tix-nocheck
Suppresses all diagnostics for the entire file. Place anywhere in the file:
# tix-nocheck
{ config, lib, pkgs, ... }:
{
# This file will not report any type errors
services.foo.enable = 42;
}
# tix-ignore
Suppresses diagnostics on the next line only:
let
# tix-ignore
x = (1 + 2).foo; # no error reported for this line
y = (3 + 4).bar; # this line still reports errors
in
x
Diagnostics
Control the severity of optional diagnostics. Currently the only configurable diagnostic is unknown_type (E014), which fires when a binding has type ?.
[diagnostics]
unknown_type = "hint" # "off", "hint", "warning", or "error" (default: "hint")
The LSP editor settings (tix.diagnostics.unknownType) take precedence over tix.toml when both are set.
Runtime stub generation
Tix can generate full NixOS, Home Manager, and pkgs stubs at runtime on first use. The result is cached in the Nix store and reused on subsequent runs.
[stubs.generate]
nixpkgs = "/nix/store/...-nixpkgs-src"
home-manager = "/nix/store/...-home-manager-src"
Each source can be a direct store path or a Nix expression:
[stubs.generate]
nixpkgs = { expr = "(builtins.getFlake (toString ./.)).inputs.nixpkgs" }
home-manager = { expr = "(builtins.getFlake (toString ./.)).inputs.home-manager" }
- nixpkgs (required) — path to nixpkgs source, or
{ expr = "..." }to evaluate - home-manager (optional) — path to home-manager source; omit to skip HM stubs
- nixos-modules (optional) — extra NixOS modules appended to the option-tree eval. Their
options.*declarations show up in the generatednixos.tix, soconfig.myproj.databaseUrlis typed, not just upstream NixOS options. - home-manager-modules (optional) — same, for Home Manager.
[stubs.generate]
nixpkgs = { expr = "(builtins.getFlake (toString ./.)).inputs.nixpkgs" }
nixos-modules = [
"./modules/myproj.nix",
{ expr = "(builtins.getFlake (toString ./.)).nixosModules.default" },
]
Entries can be plain path strings (relative to tix.toml) or { expr = "..." } for modules sourced from a flake.
Debugging the extracted schema: the equivalent manual CLI invocation is tix stubs generate nixos --flake . --hostname <NAME> — this evaluates a live NixOS configuration (including your user modules) and emits the stub to stdout or -o <path>, so you can inspect exactly what the schema extractor sees before letting the LSP drive it. Home Manager has an equivalent tix stubs generate home-manager --flake . --username <NAME>.
Module edits are not auto-detected. After changing any file listed under nixos-modules / home-manager-modules, run tix stubs refresh and restart the LSP.
Custom module systems
For arbitrary module systems (flake-parts, devenv, nix-darwin, custom evalModules setups), add a [stubs.generate.systems.<name>] table:
[stubs.generate.systems.flake-parts]
options_expr = "(inputs.flake-parts.lib.evalFlakeModule {...} {...}).options"
context_args = ["config", "lib", "pkgs"]
Each custom system generates a <name>.tix file that you reference in contexts as @<name>. Types are assigned by name: config → <Name>Config, lib → Lib, pkgs → Pkgs, any other name → { ... }. For precise types on extra args (e.g. flake-parts withSystem), layer a hand-written .tix after @<name> in the context’s stubs array — see Typing extra module args precisely. Start by prototyping the options_expr manually with tix stubs generate module — see Custom module systems for the full CLI-first iteration workflow.
On first run, tix invokes nix build to generate .tix stubs from the NixOS option tree, Home Manager options, and nixpkgs package set. This takes 30-60 seconds. Subsequent runs are instant thanks to a lightweight file cache (~/.cache/tix/store-stubs/). Changing either nixpkgs or tix version triggers regeneration.
[stubs.generate] can coexist with manual stub paths:
[stubs]
paths = ["./my-extra-stubs/"]
[stubs.generate]
nixpkgs = { expr = "(builtins.getFlake (toString ./.)).inputs.nixpkgs" }
Resolution priority:
TIX_BUILTIN_STUBSenv var (always wins)[stubs.generate]runtime generation- Compiled-in minimal stubs
Requirements: The tix binary must be installed via Nix (running from /nix/store/...). In dev mode (cargo build), use TIX_BUILTIN_STUBS or nix build .#stubs instead.
Generating tix.toml
Run tix init to automatically generate a tix.toml for your project:
tix init # Generate tix.toml in current project
tix init --dry-run # Preview without writing
tix init --yes # Overwrite existing tix.toml
tix init /path/to/project # Specify project directory
The command scans all .nix files, classifies each by its structural signals (parameter names, body references, attrset keys), and generates context sections mapping file paths to the appropriate stubs. For flake projects, it also auto-detects nixpkgs and home-manager inputs from flake.lock and generates the [stubs.generate] section.
No-module escape hatch
If tix incorrectly treats a file as a module, add this comment to disable module-aware features:
/** no-module */