Antora Troubleshooting

Overview

This runbook documents common Antora build issues encountered across the domus-* documentation repositories and their solutions.

Attribute Reference Warnings

Problem

Antora/AsciiDoctor interprets text within curly braces {…​} as attribute references. When an attribute doesn’t exist, you get warnings like:

[WARN] (asciidoctor): skipping reference to missing attribute: n
    file: .../vim-neovim-reference.adoc

Common Patterns That Trigger Warnings

Pattern Context Example

{n}

Vim commands (repeat count)

{n}dd - delete n lines

{operator}, {object}

Vim text objects

{operator}\{a|i\}{object}

${VAR}

Shell variables in code blocks

${VM}.xml, ${ISE_API_USER}

{placeholder}

Documentation placeholders

CFG{version}, {prefix}

{id}, {policyId}

API URL path parameters

/ers/config/endpoint/{id}

Solution: Escape Curly Braces

Escape curly braces with backslash to prevent AsciiDoc interpretation:

Before (triggers warning) After (escaped)

{n}dd

{n}dd

${VM}.xml

$\\{VM\\}.xml

{operator}\{a|i\}{object}

{operator}\{a|i\}{object}

/endpoint/{id}

/endpoint/\\{id\\}

The backslash goes before each curly brace, not before the dollar sign:

  • Wrong: \\${VAR} (escapes $ in shell, not { in AsciiDoc)

  • Correct: ${VAR} (escapes both braces for AsciiDoc)

Finding Attribute Warnings

Run the Antora build and grep for warnings:

make 2>&1 | grep -E "WARN.*missing attribute"

Or for detailed output:

npx antora antora-playbook-local.yml 2>&1 | grep -A2 "WARN"

Fixing Attribute Warnings

  1. Identify the file and attribute name from the warning

  2. Search for unescaped occurrences:

    grep -n '{attributename}' path/to/file.adoc
  3. Edit the file to escape curly braces:

    # Single occurrence
    sed -i 's/{n}/\\{n\\}/g' file.adoc
    
    # Or use your editor
  4. Rebuild and verify:

    make 2>&1 | grep WARN

Code Block Delimiter Issues

Problem

AsciiDoc uses ---- for listing blocks. If your code content contains ---- (e.g., PowerShell table output), the parser gets confused:

ERROR: level 0 sections can only be used when doctype is book
ERROR: unterminated listing block

Solution: Use Literal Blocks

Change from listing block (----) to literal block (…​.) for content containing dashes:

Before (breaks):

[source]
----
Name        Status
----        ------
MyObject    OK
----

After (works):

[source]
....
Name        Status
----        ------
MyObject    OK
....

When to Use Each Delimiter

Delimiter Block Type Use Case

----

Listing block

Code without ---- in content

…​.

Literal block

Code/output containing ---- or tables

====

Example block

Admonition content, nested examples

Cross-Reference (xref) Errors

Cross-reference errors are the most common build issues. This section covers all scenarios.

Error Format

[ERROR] (asciidoctor): target of xref not found: component::path/to/file.adoc
    file: /path/to/source-file.adoc
    source: /path/to/repo (branch: main)

Quick Diagnosis Flowchart

Xref Troubleshooting

Issue 1: Single vs Double Colon

Symptom: target of xref not found: domus-infra-ops:runbooks/file.adoc

Cause: Antora xrefs require double colons (::) between component and page.

Fix:

Wrong Correct

`xref:domus-infra-ops:runbooks/file.adoc[`]

`xref:infra-ops::runbooks/file.adoc[`]

`xref:component:page.adoc[`]

`xref:component::page.adoc[`]

Issue 2: Wrong Component Name

Symptom: target of xref not found: domus-ise-linux:page.adoc

Cause: The component name comes from antora.yml, not the repo name.

Fix: Look up the actual component name:

# Find component names
grep "^name:" ~/atelier/_bibliotheca/domus-*/docs/asciidoc/antora.yml

Component Name Reference:

Repository Component Name (use in xref)

domus-docs

home::

domus-infra-ops

infra-ops::

domus-ise-linux

ise-linux::

domus-ise-ops

ise-ops::

domus-netapi-docs

netapi::

domus-secrets-ops

secrets-infrastructure::

domus-linux-ops

linux-ops::

domus-identity-ops

identity-ops::

domus-python

python-ops::

domus-automation-ops

automation-ops::

Issue 3: Component Not in Playbook

Symptom: target of xref not found: identity-ops::index.adoc (but component exists)

Cause: The referenced component isn’t included in the playbook’s content.sources.

Fix: Add the missing component to the playbook:

content:
  sources:
    # ... existing sources ...
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
      start_path: docs/asciidoc
      branches: HEAD

Check what’s in a playbook:

grep -A1 "url:" docs/asciidoc/antora-playbook.yml

Issue 4: Page Doesn’t Exist

Symptom: target of xref not found: infra-ops::services/keycloak.adoc

Cause: The target page doesn’t exist in the component.

Fix:

  1. Check if the page exists:

    ls ~/atelier/_bibliotheca/domus-infra-ops/docs/asciidoc/modules/ROOT/pages/services/
  2. If not, point to an existing page (usually the component index):

    # Instead of non-existent page
    xref:infra-ops::services/keycloak.adoc[Keycloak]
    
    # Point to component root
    xref:infra-ops::index.adoc[Infrastructure Operations]

Issue 5: Relative Paths in Subdirectories

Symptom: target of xref not found: hardened-dacl.adoc (file exists in same directory)

Cause: Antora xrefs are always relative to pages/, not the current file’s directory.

Fix: Use the full path from pages/:

File Location Wrong xref Correct xref

pages/03-ise-config/research.adoc

`xref:hardened-dacl.adoc[`]

`xref:03-ise-config/hardened-dacl.adoc[`]

pages/network/index.adoc

`xref:dot1x-switches.adoc[`]

`xref:network/dot1x-switches.adoc[`]

pages/runbooks/backup.adoc

`xref:disaster-recovery.adoc[`]

`xref:runbooks/disaster-recovery.adoc[`]

Issue 6: Duplicate Component in Playbook

Symptom:

FATAL (antora): Duplicate nav in 2026@prj-ise-home-linux: modules/ROOT/nav.adoc
    1: .../domus-ise-linux/docs/asciidoc/modules/ROOT/nav.adoc
    2: .../domus-ise-linux/docs/asciidoc/modules/ROOT/nav.adoc

Cause: The same repository URL appears twice in the playbook’s content.sources.

Fix: Remove the duplicate entry from the playbook:

# Find duplicates in playbook
grep "url:" docs/asciidoc/antora-playbook.yml | sort | uniq -d

Issue 7: Spoke Playbook Missing Common Components

Symptom: Build works in hub (domus-docs) but fails in spoke repos with xref errors.

Cause: Spoke playbooks don’t include all components referenced by domus-docs roadmaps.

Commonly Missing Components:

Component Required For

identity-ops

Identity federation roadmap, Keycloak xrefs

ise-ops

ISE operations xrefs, zero-trust roadmap

python-ops

Network automation roadmap, Python tooling xrefs

Fix: Add all three to every spoke playbook:

    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-ise-ops
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-python
      start_path: docs/asciidoc
      branches: HEAD

Issue 8: Spoke Playbook Missing Hub and All Cross-References

Symptom: Multiple xref errors for various components when building a spoke repo.

Cause: Some spoke playbooks were created for truly standalone builds (only their own component) and never updated to include the hub or cross-referenced components.

Diagnostic:

# Check if playbook only lists itself
grep -c "url:" docs/asciidoc/antora-playbook.yml
# If result is 1, playbook needs all components added

Fix: Add the complete component set:

content:
  sources:
    # This project
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-YOUR-REPO
      start_path: docs/asciidoc
      branches: HEAD
    # Domus hub
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-docs
      start_path: docs
      branches: HEAD
    # Cross-referenced components
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-infra-ops
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-ise-linux
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-netapi-docs
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-secrets-ops
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-ise-ops
      start_path: docs/asciidoc
      branches: HEAD
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-python
      start_path: docs/asciidoc
      branches: HEAD

Batch Fix Commands

Find all broken xrefs in build output:

make 2>&1 | grep -E "target of xref not found" | sort -u

Find xrefs with single colon (wrong syntax):

grep -rn 'xref:domus-[a-z-]*:' docs/
grep -rn 'xref:[a-z-]*:[^:]' docs/  # Single colon not followed by colon

Find xrefs in a specific repo:

grep -rn "xref:" ~/atelier/_bibliotheca/domus-docs/docs/modules/ROOT/pages/

Adding Components to All Spoke Playbooks

When adding a new component, update playbooks in all spoke repos:

# List all spoke repo playbooks
ls ~/atelier/_bibliotheca/domus-*/docs/asciidoc/antora-playbook.yml

# Add to each (example for domus-identity-ops)
cat >> ~/atelier/_bibliotheca/domus-ise-linux/docs/asciidoc/antora-playbook.yml << 'EOF'
    - url: /home/evanusmodestus/atelier/_bibliotheca/domus-identity-ops
      start_path: docs/asciidoc
      branches: HEAD
EOF

Prevention: xref Syntax Reference

// Same component, same module
xref:other-page.adoc[Link Text]

// Same component, subdirectory
xref:subdir/page.adoc[Link Text]

// Different component
xref:component::page.adoc[Link Text]

// Different component, subdirectory
xref:component::subdir/page.adoc[Link Text]

// Different component, different module
xref:component:module:page.adoc[Link Text]

Documenting xref Examples

When writing documentation that shows xref syntax examples, use one of these methods to prevent interpretation:

  1. Literal blocks (…​.) - Best for multi-line examples

  2. Passthrough () - Best for inline examples in tables

  3. Backticks inside pass - \`xref:...[\]`

[source] and [source,asciidoc] blocks do NOT prevent xref interpretation. Always use literal blocks (…​.) for xref examples.

Cloudflare Access Blocking Static Assets

Problem

When an Antora site is protected by Cloudflare Access (Zero Trust), the page may appear white or unstyled even though the HTML loads correctly.

Symptoms:

  • Page loads but has no CSS (white background, unstyled text)

  • Browser console shows 302 redirects for //css/site.css, //js/site.js

  • document.documentElement.getAttribute('data-theme') returns the correct value but styles don’t apply

  • Preview deployments work, but custom domain doesn’t

Cause

Cloudflare Access protects the entire domain, including static assets at /_/*. When the browser requests CSS/JS files, Access redirects them to the login page instead of serving the files.

Diagnostic:

curl -sI "https://docs.domusdigitalis.dev/_/css/site.css" | head -5

# If you see HTTP/2 302 with location: cloudflareaccess.com -> Access is blocking
# Should be HTTP/2 200 for CSS to load

Solution

Create a separate Access application for the static assets path with a bypass policy:

Option 1: Using netapi (recommended)

# One command creates the app and bypass policy
netapi cloudflare access bypass-static docs.domusdigitalis.dev

Option 2: Using curl

# Step 1: Create Access app for static path
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "domus-docs-static",
    "type": "self_hosted",
    "domain": "docs.domusdigitalis.dev/_/*",
    "session_duration": "24h"
  }'

# Step 2: Add bypass policy (use app ID from step 1)
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/access/apps/<APP_ID>/policies" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Bypass Static Assets",
    "decision": "bypass",
    "include": [{"everyone": {}}]
  }'

Verify fix:

curl -sI "https://docs.domusdigitalis.dev/_/css/site.css" | head -1
# Should show: HTTP/2 200

Clear browser cache (Ctrl+Shift+R) after applying the fix.

Security Note

The /_/* path only contains static assets (CSS, JS, fonts, images). These are the same assets in the public Antora UI bundle - not sensitive documentation content.

For detailed steps, see: Cloudflare Access + Antora Runbook

Build Performance

Slow Builds

For local development, use the local playbook which reads from worktrees instead of fetching:

npx antora antora-playbook-local.yml

Cache Issues

Clear the Antora cache if seeing stale content:

rm -rf .cache/antora
npx antora --fetch antora-playbook.yml

Reference

Repositories in Aggregation

Repository Component Content

domus-docs

home

Aggregator, hub landing page, roadmaps

domus-infra-ops

infra-ops

Infrastructure runbooks, PKI, services

domus-linux-ops

linux-ops

Linux commands, KVM, editors

domus-netapi-docs

netapi

netapi CLI documentation

domus-ise-linux

ise-linux

802.1X EAP-TLS for Linux

domus-ise-ops

ise-ops

ISE operations, policies, integration

domus-ise-windows

ise-windows

802.1X EAP-TLS for Windows

domus-secrets-ops

secrets-infrastructure

Secrets management (dsec, gopass)

domus-identity-ops

identity-ops

Keycloak, SAML, identity federation

domus-python

python-ops

Python CLI tools, automation

domus-automation-ops

automation-ops

Jinja2 templates, GitOps patterns

domus-captures

captures

Worklogs, session notes

domus-siem-ops

siem-ops

Wazuh, QRadar, Sentinel, threat detection

domus-o11y-ops

o11y

Prometheus, Grafana, Loki observability

domus-windows-ops

windows-ops

PowerShell, WSL, Windows certificates

Useful Commands

# Build with local worktrees
make

# Build with remote fetch
npx antora --fetch antora-playbook.yml

# Check for all warnings
make 2>&1 | grep -E "(WARN|ERROR)"

# Find unescaped attributes in a repo
grep -rn '{[a-z_]*}' docs/asciidoc/modules/ROOT/pages/