Table of Contents

zkdoc (on-premises)

One of Zeugwerk's CI/CD tools for TwinCAT. zkdoc is the on-premises command-line tool that generates API documentation from TwinCAT PLC source code and produces HTML sites via DocFX. It is the self-hosted counterpart to zkdoc-action.

Use zkdoc when you need to generate documentation in a private or air-gapped environment, integrate with a pipeline that does not use GitHub/GitLab/Bitbucket Actions, or customize the output extensively with your own DocFX templates.

For cloud-based doc generation with no local setup, use zkdoc-action instead.

Comment syntax - How to write documentation comments in your PLC source: Syntax reference.


Availability

Option When to use
zkdoc-action Public repositories on GitHub, GitLab, or Bitbucket. Free tier (30 builds/month).
zkdoc CLI (this page) Private repos, on-premises pipelines, or any CI that runs Windows executables. Requires a license.

Contact us for licensing, trials, and on-premises options.


Requirements

Requirement Notes
Windows Required; zkdoc runs as a Windows .NET executable.
.NET runtime Bundled with the zkdoc distribution.
License file license.lic must be placed in the same folder as zkdoc.exe. Trial available on request.
DocFX (optional) Required only for HTML output. Download from dotnet.github.io/docfx.

Installation & licensing

On Windows, install Scoop and the Zeugwerk bucket with the bootstrap script, then install zkdoc. Scoop puts zkdoc on PATH.

$env:ZEUGWERK_TOKEN = '<your token>'
irm https://zeugwerk.dev/scoop/bootstrap.ps1 | iex
scoop install zkdoc

Place license.lic (provided by Zeugwerk) in the same folder as zkdoc.exe. Optionally install DocFX and add docfx.exe to your PATH (DocFX is also in the Zeugwerk bucket: scoop install docfx).

Updates, pinning a version, and CI housekeeping: Installing On-Premises Tools.

Verify the installation:

zkdoc.exe --version

Quick start

Point zkdoc at your .plcproj file and it extracts documentation, writes Markdown, and sets up a DocFX project:

zkdoc.exe --docfx --markdown -o "C:\output\docs" "C:\repos\MyProject\MyPLC\MyPLC.plcproj"

Then build the HTML site with DocFX:

docfx.exe build "C:\output\docs\docfx.json"

Serve locally for preview:

docfx.exe serve "C:\output\docs\docfx.json"

Writing documentation comments

zkdoc extracts documentation written directly in your PLC source using /// and //< comment delimiters. The same syntax works with zkdoc-action in the cloud.

For the full reference - delimiter rules, visibility filters, \copydoc, cross-references, diagrams, and code formatting - see the Comment syntax reference.


Usage

zkdoc.exe [filepaths...] [options]

filepaths can be:

  • One or more paths to .plcproj files.
  • A directory that contains a .Zeugwerk/config.json (the project is resolved automatically). Pass . for the current directory.
  • A modules root (a config.json that lists modules instead of solution/projects). zkdoc expands those modules the same way zkmake does, so . stays valid for monorepos. See config.json modules.

CLI reference

Option Short Default Description
filepaths (current dir) Paths to .plcproj files or directories containing .Zeugwerk/config.json. Multiple paths can be listed; zkdoc combines them into one documentation set. A modules-only root expands config.modules automatically.
--modules (all modules) Comma-separated subset of root config.json modules to document. Order follows the root list. Omit to document every module. Same meaning as zkmake build --modules. Ignored when the config has no modules list.
--output -o documentation Root output directory. Created if it does not exist. All generated files are written under this path.
--docfx - Subfolder name inside --output for DocFX Markdown and the docfx.json project file (e.g. reference). Required to produce HTML with DocFX.
--markdown - Subfolder name inside --output for plain Markdown reference files (no DocFX dependency). Can be used independently of --docfx.
--filter -f ((_)*Test[s]*\\.*?\|.*?\\(_)*Test[s]*\\.*?\|.*?\\(_)*Example[s]*\\.*?) Regular expression to exclude source paths. Matched against the full file path. Files whose path contains _Test, _Tests, or _Examples as a subfolder are excluded by default.
--include-internal false Include objects marked with the INTERNAL access modifier or the {attribute 'hide'} pragma. Excluded by default.
--exclude-private false Exclude objects and methods with the PRIVATE access modifier. Included by default.
--exclude-protected false Exclude objects and methods with the PROTECTED access modifier. Included by default.
--depth 1 Maximum category depth shown in the navigation sidebar of the generated site. Increase for projects with deep namespaces.
--twincat-source-target src Subfolder inside --output where TwinCAT PLC source files are extracted.
--telemetry false Send anonymous usage data to Zeugwerk (install ID, tool name, version).

Output structure

After running zkdoc --docfx --markdown -o documentation MyPLC.plcproj, the output directory contains:

documentation/
├── docfx.json              ← DocFX project file (generated once; not overwritten if it exists)
├── toc.yml                 ← Top-level table of contents
├── index.md                ← Landing page (generated if not present)
├── reference/              ← DocFX Markdown (--docfx)
│   ├── toc.yml
│   └── <Namespace>/
│       ├── <FunctionBlock>.md
│       ├── <Interface>.md
│       └── ...
├── md-reference/           ← Plain Markdown (--markdown), no DocFX deps
│   └── ...
├── userguide/              ← Placeholder user guide (generated if not present)
│   ├── introduction.md
│   └── toc.yml
└── html/                   ← Final HTML output (produced by DocFX build)
    └── ...
Note

If docfx.json already exists in the output folder, zkdoc does not overwrite it. This lets you maintain a custom template or adjusted configuration across runs.


Examples

Single PLC, full HTML site

zkdoc.exe --docfx --markdown -o docs MyProject\MyPLC\MyPLC.plcproj
docfx.exe build docs\docfx.json
docfx.exe serve docs\docfx.json

Multiple PLCs in one documentation set

List all .plcproj paths; zkdoc merges them into a single API reference:

zkdoc.exe --docfx --markdown -o docs ^
  src\LibraryA\LibraryA.plcproj ^
  src\LibraryB\LibraryB.plcproj

Zeugwerk / Twinpack project (auto-discovery)

Pass the repository root (.) and zkdoc uses .Zeugwerk/config.json to discover PLC projects:

zkdoc.exe --docfx --markdown -o docs .

That works for a normal solution root (solution + projects) and for a modules root (only modules). In the modules case zkdoc walks each listed folder and documents the PLCs found there, so existing pipelines that pass filepath: '.' / . keep working without listing every module path.

Monorepo: document a subset of modules

Same filter as zkmake build --modules. Paths must match entries in the root config.modules list; order follows that list:

:: Document two libraries and one application
zkdoc.exe --docfx --markdown -o docs --modules modules/LibraryA/src,modules/LibraryB/src,apps/MachineA/src .

Exclude additional folders

Use --filter to extend the default exclusions with your own pattern:

:: Exclude _Internal and _Internals folders as well
zkdoc.exe --docfx -o docs --filter "((_)*Internal[s]*\\.*?)" MyPLC.plcproj

Include internal members

zkdoc.exe --docfx -o docs --include-internal MyPLC.plcproj

Exclude private and protected members

zkdoc.exe --docfx -o docs --exclude-private --exclude-protected MyPLC.plcproj

Deeper namespace hierarchy

zkdoc.exe --docfx -o docs --depth 3 MyPLC.plcproj

Customizing documentation output

For adding custom Markdown pages, adjusting the folder layout, and customizing the DocFX HTML template, see the Customizing documentation output reference. The same options apply to zkdoc-action.


Integration with CI/CD pipelines

zkdoc requires a Windows agent with TwinCAT XAE and DocFX installed. The pipeline generates Markdown from the PLC source, then DocFX converts it to HTML. The HTML output can be archived as a build artifact or deployed to a web server / GitHub Pages.

Jenkins (Declarative Pipeline)

pipeline {
    agent { label 'windows-twincat' }

    stages {
        stage('Generate documentation') {
            steps {
                // Generate DocFX Markdown from PLC source into the documentation/ folder.
                bat "zkdoc.exe --docfx reference --output documentation ."
                // Build the HTML site from the generated DocFX project.
                bat "docfx.exe build documentation\\docfx.json"
            }
        }
        stage('Publish') {
            steps {
                publishHTML(target: [
                    reportDir: 'documentation/html',
                    reportFiles: 'index.html',
                    reportName: 'API Documentation'
                ])
            }
        }
    }

    post {
        always {
            archiveArtifacts artifacts: 'documentation/html/**/*', allowEmptyArchive: true
        }
    }
}

Azure DevOps

jobs:
  - job: Docs
    pool:
      name: 'windows-twincat-agents'
    steps:
      - script: |
          zkdoc.exe --docfx reference --output documentation .
          docfx.exe build documentation\docfx.json
        displayName: Generate documentation

      - task: PublishPipelineArtifact@1
        inputs:
          targetPath: documentation/html
          artifact: api-docs

Troubleshooting

Symptom Likely cause Fix
License verification failed license.lic missing or expired Place license.lic next to zkdoc.exe. Contact us for a trial or renewal.
Invalid arguments or no output No valid .plcproj or config.json found Verify the filepath argument and that the file exists.
No PLC could be loaded Path has no PLCs and no expandable modules, or --modules matched nothing For a monorepo root, ensure config.json lists modules and each module folder has its own .Zeugwerk/config.json. Check --modules spelling against the root list.
--modules '…' did not match any entry Filter does not match config.modules Use the same paths as in the root config.json (comma-separated).
Empty reference section All POUs matched by the filter Check --filter. Run with --include-internal to see if items are being hidden by access modifiers.
DocFX build errors Malformed Markdown or missing references Check zkdoc console output for parse warnings. Missing \copydoc targets print an explicit error message.
HTML layout broken Custom docfx.json references a missing template Verify template names in docfx.json. The default templates are default, modern, template, and statictoc.
docfx.json not updated Intentional - zkdoc never overwrites an existing docfx.json Edit docfx.json manually, or delete it to let zkdoc regenerate it.

Summary

  1. Install zkdoc and place license.lic next to the executable.
  2. Optionally install DocFX for HTML output.
  3. Run zkdoc.exe --docfx --markdown -o <out> <path-to.plcproj>.
  4. Run docfx.exe build <out>/docfx.json and open the html/ folder.
  5. Add custom Markdown pages to userguide/ and link them in toc.yml.

For comment syntax, cross-references, diagrams, and code formatting: Syntax reference.
For cloud-based generation without a local setup: zkdoc-action.