{
  "generator": {
    "name": "Symvanta",
    "url": "https://symvanta.com"
  },
  "page": {
    "url": "https://symvanta.com/architecture/caddy",
    "title": "Caddy Architecture: How It Actually Works",
    "description": "How the Caddy codebase is structured: module map, load-bearing symbols, and dependency structure, generated by Symvanta.",
    "datePublished": "2026-08-11",
    "dateModified": "2026-08-11"
  },
  "repository": {
    "name": "caddyserver/caddy",
    "url": "https://github.com/caddyserver/caddy",
    "commit": "947087c",
    "license": "Apache-2.0"
  },
  "graph": {
    "modules": 46,
    "dependencyCycles": 0,
    "modularityQ": 0.79,
    "largestCycleFiles": null,
    "mutuallyRecursiveGroups": 6
  },
  "summaryMarkdown": "Caddy is a web server built as a module system: a small core that parses\nconfig, loads modules, and swaps configurations at runtime, with the HTTP\nserver, reverse proxy, and TLS automation all plugged in as modules. The\ngraph at `947087c` shows that split directly. Symvanta detects 46 functional\nmodules at modularity Q=0.79, and the biggest clusters are the config\nmachinery itself: the Caddyfile dispenser (513 symbols), the placeholder\nreplacer (496), and the module registry and loader (356 and 311).\n\nThat is the structural signal: in most servers the request path dominates the\nmap, and in Caddy the config path does. `RegisterModule`, `LoadModule`, and\nthe `Val` token dispenser rank in the PageRank top 10 because every plugin\nin the repo goes through them. The mutually recursive symbols tell the same\nstory: the largest group is the config-swap cycle in `caddy.go`\n(`changeConfig`, `run`, `provisionContext`, `finishSettingUp`).",
  "subsystems": [
    {
      "name": "Caddyfile dispenser",
      "descriptionMarkdown": "`Val`, `Next`, `NextArg`, and `ArgErr` on the dispenser\n(`caddyconfig/caddyfile/dispenser.go`): the token reader every Caddyfile\ndirective parses itself with. 513 symbols, the largest cluster in the repo,\nand its edges fan out to the httpcaddyfile adapter, the registry, and the\ncore."
    },
    {
      "name": "Placeholder replacer",
      "descriptionMarkdown": "`NewReplacer` and the `{placeholder}` substitution engine from `replacer.go`.\n496 symbols. Placeholders reach into every layer, so this cluster talks to\nten others, with its heaviest edge into the HTTP server internals."
    },
    {
      "name": "Core logging and network addresses",
      "descriptionMarkdown": "The `caddy` package's shared surface: the `Log` global logger accessor\n(`logging.go`), network address parsing (`JoinNetworkAddress`,\n`ParseNetworkAddressWithDefaults`), and config adapters. 445 symbols."
    },
    {
      "name": "Module and directive registry",
      "descriptionMarkdown": "`RegisterModule` (`modules.go`) plus `RegisterDirective` and\n`RegisterHandlerDirective`: the init-time registry every plugin registers\ninto. 356 symbols."
    },
    {
      "name": "LoadModule and provisioning",
      "descriptionMarkdown": "`Context.LoadModule` and `LoadModuleByID` (`context.go`): the reflection\nwalk that turns raw JSON config into provisioned module instances. 311\nsymbols, and its heaviest edge runs into the core cluster (61 calls)."
    },
    {
      "name": "Context and metrics",
      "descriptionMarkdown": "`NewContext` (`context.go:65`) with the metrics registry and the\ninstrumented-route wrappers. 210 symbols."
    },
    {
      "name": "Shared infrastructure",
      "descriptionMarkdown": "The rest of the top 10: the integration test harness (206 symbols, hub\n`NewTester`), HTTP server internals (195, hub\n`provisionHeaderAliasAllowlist` from `modules/caddyhttp/server.go`), the\nhttpcaddyfile adapter (166, hub `Name`), and the reverse proxy upstreams\n(164, whose hub `resetDynamicHosts` is a test helper, which is what a hub\nlooks like when the tests drive every upstream path)."
    }
  ],
  "startReading": {
    "symbols": [
      {
        "name": "Log"
      },
      {
        "name": "Val"
      },
      {
        "name": "RegisterModule"
      },
      {
        "name": "LoadModule"
      },
      {
        "name": "NewContext"
      },
      {
        "name": "provisionHeaderAliasAllowlist"
      },
      {
        "name": "determineTrustedProxy"
      },
      {
        "name": "allTokens"
      }
    ],
    "endpoints": []
  },
  "requestFlow": [
    {
      "position": 1,
      "symbol": "changeConfig (caddy.go:158)",
      "sourceUrl": null,
      "markdown": "`changeConfig (caddy.go:158)`"
    },
    {
      "position": 2,
      "symbol": "unsyncedDecodeAndRun (caddy.go:337)",
      "sourceUrl": null,
      "markdown": "`unsyncedDecodeAndRun (caddy.go:337)`"
    },
    {
      "position": 3,
      "symbol": "run (caddy.go:419)",
      "sourceUrl": null,
      "markdown": "`run (caddy.go:419)`"
    },
    {
      "position": 4,
      "symbol": "provisionContext (caddy.go:484)",
      "sourceUrl": null,
      "markdown": "`provisionContext (caddy.go:484)`"
    },
    {
      "position": 5,
      "symbol": "Context.LoadModule",
      "sourceUrl": null,
      "markdown": "`Context.LoadModule`"
    },
    {
      "position": 6,
      "symbol": "LoadModuleByID",
      "sourceUrl": null,
      "markdown": "`LoadModuleByID`"
    },
    {
      "position": 7,
      "symbol": "finishSettingUp",
      "sourceUrl": null,
      "markdown": "`finishSettingUp`"
    }
  ],
  "diagrams": [
    {
      "url": "https://symvanta.com/architecture/caddy/module-map.svg",
      "encodingFormat": "image/svg+xml"
    }
  ]
}
