# loadbearing [Health: Active]

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/andypgray/loadbearing  
**GitHub Stars:** 0  
**npm Downloads (last month):** 14  
**Views:** 2  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/loadbearing

## Description
Checks .NET solutions against a fluent C# architecture spec and explains the rules to agents.

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "loadbearing": {
    "command": "npx",
    "args": ["-y","loadbearing"]
  }
}
```

## Documentation & README

# LoadBearing

<!-- mcp-name: io.github.andypgray/loadbearing -->

[![CI](https://github.com/andypgray/loadbearing/actions/workflows/ci.yml/badge.svg)](https://github.com/andypgray/loadbearing/actions/workflows/ci.yml) [![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/andypgray/loadbearing?label=openssf+scorecard)](https://scorecard.dev/viewer/?uri=github.com/andypgray/loadbearing) [![NuGet](https://img.shields.io/nuget/v/Zphil.LoadBearing.Cli?logo=nuget&label=nuget)](https://www.nuget.org/packages/Zphil.LoadBearing.Cli) [![NuGet downloads](https://img.shields.io/nuget/dt/Zphil.LoadBearing.Cli?label=downloads)](https://www.nuget.org/packages/Zphil.LoadBearing.Cli)

LoadBearing is a .NET architecture checker that enforces one C# spec and renders the same rules for coding agents.

A long-lived codebase has layers, boundaries and rules. They live in a few heads, no build step checks them, and the diagrams drift. Nothing fails when a change crosses a boundary. A coding agent makes that change quickly and plausibly, and it cannot see which walls are load-bearing. LoadBearing's answer is architecture-as-code. The rules become one C# spec, and the spec produces every surface this page names.

1. **Enforcement.** One checker passes or fails the rules at the command line, in CI and as named xUnit tests. An agent hook runs the same check when the agent's turn ends.
2. **Agent context.** The rules render to a managed `AGENTS.md` block, per-directory rule cards and MCP query tools for coding agents.

Write your architecture once. Use it everywhere.

LoadBearing is pre-alpha. Minor versions can still change the spec API before 1.0. [Status](#status) holds the current inventory.

Install the tool, build your solution, and check it:

```bash
dotnet tool install -g Zphil.LoadBearing.Cli
dotnet build MyApp.sln
loadbearing check MyApp.sln
```

A rule is one statement:

```csharp
arch.Rule("layering/domain-independent")
    .Enforce(domain.MustNotReference(application, infrastructure, api))
    .Because("The Domain holds the quote and rate model the rest of the subsystem is built on; it stays free of the layers that depend on it so it can be reasoned about and tested on its own.")
    .Fix("Move the dependency out of Domain: define an interface here and implement it in the outer layer that needs it.");
```

That is the whole rule. It has an ID, a posture, a constraint, a reason and a fix. Here the posture is `Enforce`. The rule is committed in the [clean-architecture example](https://github.com/andypgray/loadbearing/tree/main/examples/Meridian.Quoting), and CI holds `check` green against the codebase it governs.

## When an agent breaks a rule

Rules like the one above govern this repository too. One of them keeps the CLI off stdout, because the MCP server speaks JSON-RPC over that channel. Suppose an agent adds a progress printer to the CLI and reaches for `Console.WriteLine`. When it tries to hand the work back, the `Stop` hook in [`hooks/`](https://github.com/andypgray/loadbearing/tree/main/hooks) runs `check` over the working tree, the rule goes red, and the hook refuses the stop with this report on the agent's stderr:

```text
FAIL cli/no-stdout — The Host layer must not use `Console.Out`, `Console.Write()` or `Console.WriteLine()`.
  because: Stdout is a protocol channel here — the MCP server speaks JSON-RPC over it and CLI output flows through System.CommandLine's console — so a direct Console write corrupts the wire and is invisible to the in-process tests.
  citation: https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio
  fix: Write CLI output through the command's InvocationConfiguration console; route server diagnostics to the logger or Console.Error.
  subject: 160 types, 1 generated
  src/Zphil.LoadBearing.Cli/Rendering/ProgressPrinter.cs:10 — Zphil.LoadBearing.Cli.Rendering.ProgressPrinter uses System.Console.WriteLine()
  src/Zphil.LoadBearing.Cli/Rendering/ProgressPrinter.cs:15 — Zphil.LoadBearing.Cli.Rendering.ProgressPrinter uses System.Console.WriteLine()
```

Every line of that report is something the agent can act on without asking a human. The agent routes the output through the command's console instead, and the next stop is clean.

[LoadBearing on itself](https://github.com/andypgray/loadbearing/blob/main/SELF-CHECK.md) shows this repository's own spec on the other surfaces too, from the committed rule and the prose it renders to the xUnit run, code scanning and the graph. It also says how every excerpt on both pages is held to its source.

## The four postures

Every rule carries one of the first two, and every scope one of the last two.

| Posture | What it is | What fails |
|---|---|---|
| `Enforce` | the law | every violation, even ones predating the rule |
| `Migrate` | a ratchet over a counted baseline | new violations; baselined sites stay quiet |
| `Quarantine` | containment for a scope | a new reference into the scope |
| `Caution` | dragons for code new callers are welcome to | nothing; a change set touching it draws a warning |

`Enforce` failing violations that predate it is what `Migrate` exists for: `loadbearing baseline` records a rule's current violations, new ones fail from the next commit, and the baseline only shrinks. At zero, the tool suggests promoting the rule to `Enforce`. Every scope carries a diff-aware tripwire: with `check --diff-base <ref>`, a change set that touches the scope draws a warning. For a `Caution` scope that tripwire is the whole posture: the dragons prose lands on the scope's directory as a card, `explain` and `arch_context` serve it, and no reference into the scope is ever a violation.

The three postures after `Enforce` each answer a way a coding agent goes wrong on an established codebase. `Enforce` is the law the code already keeps, and a change that crosses it fails wherever the check runs. The [Meridian example](https://github.com/andypgray/loadbearing/blob/main/examples/Meridian/README.md#three-ways-an-agent-goes-wrong-here) walks three of those ways on real output.

- An agent copies the majority pattern, the statistical prior. Six of Meridian's eight controllers open a `SqlConnection`, so inline SQL reads as house style and an agent writes its next controller the same way. `Migrate` answers it: the rendered context calls that pattern debt rather than style, in words the agent reads before it writes.
- An agent tidies away a gateway, the helpful refactor. A public type inside a module invites a direct call that skips the gateway in front of it. A `Quarantine` scope makes the gateway the only sanctioned way in.
- An agent corrects load-bearing weirdness. A check-digit table that skips values looks like a bug, and making it contiguous breaks every real container number. The dragons prose on a `Quarantine` or `Caution` scope answers it. The card on that scope's directory says what the code does, which part is load-bearing, and how to call in.

## One spec produces

Each target below consumes the same reified model, and every violation report carries the rule ID, the generated rule sentence, the reason, the fix, and the exact `file:line`.

| Target | What it is |
|---|---|
| `loadbearing check` | one pass-or-fail verdict for the command line and CI |
| `check --sarif` | that verdict as SARIF 2.1.0, for code scanning |
| xUnit adapter | every rule an individually named test |
| build analyzer | a broken rule as a squiggle in the editor and a compiler warning in the build |
| `loadbearing render` | the managed `AGENTS.md` block, per-directory rule cards, and the model as a JSON file |
| `loadbearing mcp` | `arch_check`, `arch_status`, `arch_explain`, `arch_context`, and `arch_graph`, plus a `derive_spec` prompt |
| agent hook | `check` when a turn ends; a red rule refuses the stop, report on stderr |

The adapter's failure text is byte-identical to the CLI's: the two share one renderer, and a product test pins them equal. The managed block plus `loadbearing explain` are also the generated architecture documentation, written for agents first and readable by people; [LoadBearing on itself](https://github.com/andypgray/loadbearing/blob/main/SELF-CHECK.md#the-prose-it-generates) shows the gate that keeps it current.

Claude Code reads `AGENTS.md` on its own from version 2.1.277, including each per-directory card once it opens a file in that directory. It does so only where no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` sits in the directory it was started in or any directory above it. Your own `~/.claude/CLAUDE.md` does not count. A repository that already has a `CLAUDE.md` brings the managed block in with one line in that file, `@AGENTS.md`, and `render` warns when it finds such a file without the import. The import covers the root file only. The cards load beside a `CLAUDE.md` for each developer who sets Claude Code's Project instructions option to `claude-md-and-agents-md` in `/config`. That option is a personal setting, and a repository cannot commit it.

The compiler is the source of truth for your code. LoadBearing is the source of truth for your architecture.

## Where this sits next to ArchUnitNET and NetArchTest

[NetArchTest](https://github.com/BenMorris/NetArchTest) and [ArchUnitNET](https://github.com/TNG/ArchUnitNET) run architecture rules inside your unit tests, and they are good at it. LoadBearing moves the rules out of test code into one spec (architecture-as-code rather than architecture tests) and renders every surface above from it.

| Tool | What you write | Where it runs |
|---|---|---|
| NetArchTest | fluent assertions in test methods | your test runner |
| ArchUnitNET | ArchUnit-style rules in test classes | your test runner |
| LoadBearing | one spec in its own project | every target above |

The grammar comes from surveying that prior art, and [GRAMMAR.md](https://github.com/andypgray/loadbearing/blob/main/GRAMMAR.md) records each divergence. Constraints negate in the verb (`MustNotReference`), following ArchUnitNET. If you know ArchUnit's `FreezingArchRule`: what freezing does (accept a rule's current violations as a baseline) is `Migrate` with its counted baseline here. `Quarantine` contains a scope; it does not accept the scope's violations.

`Because` is mandatory. A rule without one is an invalid spec: `check` refuses to run it and reports every spec error in one pass. Even the predicate escape hatch, `Must(condition, description:)`, does not compile without its description. Every reason ships to your agents in the rendered context, and in the [Interchange example](https://github.com/andypgray/loadbearing/tree/main/examples/Meridian.Interchange) each of the twelve rules carries a `Citation` naming the learn.microsoft.com page its reason rests on. Nine of those twelve come from a shared rule pack, which is an ordinary class library of static methods: the pack owns the citation, the spec picks the posture. That pack ships in this repository as a working example. A pack is a pattern you own, so there is no registry to depend on.

## Starting on a codebase that already exists

LoadBearing is built for long-lived, business-critical .NET codebases: systems too important to rewrite, with an architecture that is real but written down nowhere. Start where the code is:

1. Write the rules the code should hold; state the ones it does not hold yet as `Migrate`.
2. `loadbearing baseline` records every current violation on a counted, committed baseline.
3. New code in the old pattern fails from the next commit; recorded sites stay quiet.
4. Migrate recorded sites as you touch them.
5. At zero, promote the rule to `Enforce`.

The first draft is usually an agent's work, not a blank file. `loadbearing mcp` ships one prompt, `derive_spec`, which walks an agent from a solution with no spec to a reviewed proposal. The recipe surveys the estate with `arch_graph`, scaffolds the spec project, drafts every hypothesis as a rule, and lets `check` count the violations that assign each rule its posture. The tool does not infer the architecture and the agent does not ratify it: every proposed rule crosses a curation gate where you accept, edit, or drop it, and recording the baseline stays a human command. [The Meridian adoption walkthrough](https://github.com/andypgray/loadbearing/blob/main/examples/Meridian/ADOPTING.md) is that recipe replayed on a committed example codebase, one real command at a time.

## What it handles

Real solutions are rarely one shape. Each row is one such shape and what the checker does with it. The [changelog](https://github.com/andypgray/loadbearing/blob/main/CHANGELOG.md) records the release each landed in.

| Shape | What the checker does |
|---|---|
| Linked source files | reads a shared file's references once per compiling project |
| Multi-targeting projects | one project; its shared types take one framework's members and hierarchy and every framework's references |
| One type name in several projects, or in a package too | follows the compiler's binding for each reference |
| Generated code | counts it per project and per rule subject |
| Polyglot solutions | names each F#, VB or SQL project it cannot read |
| A repository on central package management | the spec project scaffold builds under `Directory.Packages.props` |
| Non-SDK-style .NET Framework projects | loads them through the Framework build host; see [.NET Framework](#net-framework) |
| A binlog from a real build | `check --binlog` replays it; see [.NET Framework](#net-framework) |
| Solution filters | stamps the projects a filtered run never checked; see [Installing](#installing) |

A project in a language the checker does not read makes the model smaller: every document names it, and the run still answers. The survey, `loadbearing graph`, names the framework a multi-targeting project's shared types follow and every type name that two projects, or a project and a package, both declare. `.Authored()` on a rule's subject keeps the rule off generated code.

## .NET Framework

The tool runs on .NET 10. The codebase it checks does not have to, and neither does the spec that governs it.

A spec project can target `net48` and compile at that framework's default language level, C# 7.3. It references the same netstandard2.0 `Zphil.LoadBearing` package every other spec does, and the CLI loads the built DLL in an isolated load context. A `typeof()` anchor works from there while the anchored type's own closure stays inside netstandard2.0. Past that line, including .NET Framework types with no counterpart on .NET, a namespace pattern is the anchor, and it needs no assembly load at all.

Old project files load too. A non-SDK-style Framework project, the kind in the 2003 MSBuild XML namespace with explicit `<Reference>` items and a hand-maintained `AssemblyInfo.cs`, loads through the .NET Framework build host Roslyn ships and reports at `file:line` like anything else:

```text
FAIL data-access/no-inline-sql — Types in `Classic.*` must not reference types in `System.Data.*`.
  Classic.Billing/BillingCalculator.cs:10 — Classic.Billing.BillingCalculator references System.Data.SqlClient.SqlConnection
```

And the build server can stay where it is. `check --binlog` replays a binary log from a real build, including one produced by .NET Framework `MSBuild.exe`, so the machine that builds needs no .NET 10; only the machine that analyses does. Replaying that log and opening the workspace directly produce byte-identical output, which is what makes the replay a shortcut rather than a lesser reading.

Loading a non-SDK-style project and replaying a log from .NET Framework `MSBuild.exe` both need Windows, because that is where the Framework build host and `MSBuild.exe` come from. What the tool looks for is a `vswhere`-discoverable Visual Studio or Build Tools install carrying `MSBuild\Current\Bin\MSBuild.exe`. Where several are installed it prefers VS 2019 or 2022. When it has to take something outside that pair, it names what it took on stderr, beside any load failure. Set `LOADBEARING_VS_INSTALL_PATH` to an install root, the parent of `MSBuild\Current\Bin`, to choose one yourself. A net48 spec project carries no such requirement and builds anywhere.

## Examples

Six worked examples in [`examples/`](https://github.com/andypgray/loadbearing/tree/main/examples) share one fictional freight-forwarding company. Four are solutions: CI builds each one, holds `check` green against the committed tree, and re-renders every managed block under `examples/` to prove a zero diff. The other two walk a flow with captured output. Three are whole codebases:

- [Enforce-only clean architecture](https://github.com/andypgray/loadbearing/tree/main/examples/Meridian.Quoting): the greenfield quoting subsystem. Nine rules hold a four-layer clean architecture, and every rule runs as a named xUnit test.
- [Three of the four postures on one codebase](https://github.com/andypgray/loadbearing/tree/main/examples/Meridian): a mid-migration monolith. The law, three ratchets and their burndown, one quarantined scope.
- [Module isolation as law](https://github.com/andypgray/loadbearing/tree/main/examples/Meridian.Operations): a modular monolith. Every module directory carries its own rendered rule card, and one module is quarantined behind its facade.

Three go deeper on one surface each:

- [Microsoft guidance, enforced and cited](https://github.com/andypgray/loadbearing/tree/main/examples/Meridian.Interchange): the cookbook page. Canon sentence, spec excerpt, and real violation, rule by rule.
- [Day-one adoption on an existing codebase](https://github.com/andypgray/loadbearing/blob/main/examples/Meridian/ADOPTING.md): the full derive flow, every step a real command with real output.
- [The agent loop, closed by a hook](https://github.com/andypgray/loadbearing/tree/main/examples/Meridian/hooks): the storyboard for the loop above, walked beat by beat with captured output, plus the wrapper scripts and the paste-in hook config.

## Installing

The CLI ships as a .NET global tool; the three commands at the top of this page install it, build your solution and check it. The checker never builds the code it checks, so restore and build the solution first. A stale build gives stale verdicts.

The machine running it needs a .NET 10 SDK: commands that load a solution (`check`, `render`, `status`, `graph`, `baseline`, `mcp`) do so through MSBuildWorkspace via MSBuildLocator, and a runtime-only environment cannot host that load. The codebase under check has no version requirement of its own: LoadBearing never builds or retargets it, and it can target .NET Framework 4.8 or anything newer. The spec project compiles against one package, `Zphil.LoadBearing`, which is netstandard2.0.

Run the install from outside any repository whose `global.json` pins an SDK below 10. Inside one, `dotnet` selects the pinned SDK, which cannot install a tool built for .NET 10.

A project that fails to load is treated as a wrong model rather than a smaller one. `check`, `baseline`, `render`, `graph` and `status` all exit 2 and say which projects failed, and `--allow-workspace-diagnostics` opts into the partial model. A project whose NuGet packages did not resolve is the same wrong model reached more quietly. It loads completely, and only the edges its package references would have produced are missing, so a rule about a package reports itself inert and the run goes green. That covers a restore that ran and failed and one that never ran at all. An SDK-style project writes `obj/project.assets.json` every time it restores, so not having one is a fact about that project, while a non-SDK-style .NET Framework project never writes one and is left alone. The unresolved case gates on the same terms and takes the same opt-out. Its refusal names those projects separately and sends you to `dotnet restore`, because a project that loaded and a project that never did ask for different repairs. The xUnit adapter answers the same way in test dress: one named test fails carrying both causes, every rule case skips, and an `AllowWorkspaceDiagnostics` override opts in.

A solution filter is the opposite case: the model is smaller, and still right. `check`, `status`, `graph` and the `arch_context` tool still answer, stamping which declared projects the filtered run never checked, and the JSON documents carry them as `uncheckedProjects`. A green over a subset can no longer read as a green over the solution. The verbs that read absence as evidence refuse instead. `baseline --init`, `baseline --accept-reductions` and `render` exit 2, because the files they would write vouch for projects the run never measured, and the fix they name is to run against the solution the filter references. The narrowing is measured from what actually loaded, not from the filter's text. A selection whose references pull in the whole solution narrows nothing and prints nothing. The xUnit adapter keeps every rule verdict and skips `Workspace_LoadedCompletely`, naming what was not checked. Committed baselines, render targets, `arch_context` paths, diff resolution and the reported project paths all resolve against the solution the filter references, never against the filter's own directory. A filter kept apart from its solution answers with the same paths as the solution itself. A rule whose whole selection lives in the unchecked projects reports as skipped, naming the filter, so an empty selection does not read as a failure. The fail-on-empty defaults stand untouched for unfiltered runs.

The command is `loadbearing`. Six lockstep-versioned packages make up a release, and a spec project references `Zphil.LoadBearing` at the version `loadbearing --version` prints:

| Package | What it is |
|---|---|
| [`Zphil.LoadBearing.Cli`](https://www.nuget.org/packages/Zphil.LoadBearing.Cli) | The `loadbearing` global tool: `check`, `render`, `explain`, `status`, `graph`, `baseline`, and the MCP server (`loadbearing mcp`). |
| [`Zphil.LoadBearing`](https://www.nuget.org/packages/Zphil.LoadBearing) | The spec contract, zero dependencies. It carries an analyzer that reports a spec's bad string literals as compiler warnings while you write it. |
| [`Zphil.LoadBearing.Xunit`](https://www.nuget.org/packages/Zphil.LoadBearing.Xunit) | The xUnit adapter: every rule as an individually named test. |
| [`Zphil.LoadBearing.Analyzers`](https://www.nuget.org/packages/Zphil.LoadBearing.Analyzers) | The build analyzer: a broken rule as a squiggle in the editor and a compiler warning in `dotnet build`, read from the model file `render --model` writes. |
| [`Zphil.LoadBearing.Roslyn`](https://www.nuget.org/packages/Zphil.LoadBearing.Roslyn) | Workspace infrastructure: solution and binlog loading, the extraction cache, the baseline store. A dependency of the above, not for direct reference. |
| [`Zphil.LoadBearing.Extraction`](https://www.nuget.org/packages/Zphil.LoadBearing.Extraction) | Per-compilation extraction machinery; a dependency of the above, not for direct reference. |

## Connecting an MCP client

An MCP client launches the same tool with the `mcp` verb; the server speaks stdio. The
`.mcp.json` shape:

```json
{
  "mcpServers": {
    "loadbearing": {
      "command": "loadbearing",
      "args": ["mcp", "MyApp.sln"]
    }
  }
}
```

For Claude Code, one command writes that same entry into the project's `.mcp.json`:

```bash
claude mcp add --scope project loadbearing -- loadbearing mcp MyApp.sln
```

The solution argument is optional, and most repositories should still pass it. Without it the
server reads `LOADBEARING_SOLUTION_PATH`, and when that is unset too it walks up from its
working directory to the first ancestor holding exactly one solution file, where a `.slnf`
filter counts only when no `.sln` or `.slnx` stands beside it. That resolves nothing where the
solution sits under `src/`, and refuses as ambiguous where several sit side by side, which
between them covers most real repositories. A solution file passed as the argument beats both.
When it cannot bind, the server starts anyway and every tool call returns the reason, naming
any solution it saw one level down. Each reply also names the CLI command that reads the same
model, because rebinding the server takes a client-config edit and a relaunch. The failure is
readable in the client rather than arriving as a server that would not start. However the
server is launched, the rule from Installing still applies: restore and build the solution
first; the checker never builds, and a stale build gives stale verdicts.

`dnx` launches the server straight from nuget.org without the global install; the `--` hands
everything after it to the tool:

```bash
dotnet dnx Zphil.LoadBearing.Cli -- mcp MyApp.sln
```

This is how MCP-registry clients run the server; note the `mcp` subcommand. Spell it `dotnet dnx`
in a shell: on Windows the short form is a `.cmd`, which a POSIX shell will
not resolve. A config generated from the registry manifest passes no solution argument, so a
repository whose walk-up resolves nothing gets an unbound server. That session still works from
the CLI, but not through the installed command, because `dnx` runs the package without installing
anything: the server's replies name `dotnet dnx Zphil.LoadBearing.Cli@<version> --yes --` in front
of the verb, pinned to the build answering. Bind such a repository at install time instead: put the
solution in the config's `args` after `mcp`, or set `LOADBEARING_SOLUTION_PATH` in its `env`.

`dnx` ships with the .NET 10 SDK. A client that launches `dnx` itself needs only one installed on
the machine, because the `dnx` command picks the newest installed SDK and never reads
`global.json`. A repository pinning an older SDK therefore launches the server anyway. The
`dotnet dnx` spelling a shell needs goes through `dotnet`, which honours the nearest `global.json`.
Inside a repository pinning an SDK below 10 that spelling does not exist, so run it from outside
the repository and pass the solution's absolute path. Without any .NET 10 SDK there is no `dnx`
command, and a registry client sees a server that died before the handshake, with the reason on
stderr alone.

## Building

```bash
dotnet build Zphil.LoadBearing.slnx
dotnet test Zphil.LoadBearing.slnx
```

## Status

These parts exist: the reified model, the fluent builder, Roslyn extraction, the CLI verbs, the SARIF writer, the xUnit adapter, the build analyzer, the MCP server, and the render pipeline, with all four postures evaluating. [LoadBearing on itself](https://github.com/andypgray/loadbearing/blob/main/SELF-CHECK.md) shows them running over this repository's own code, and CI uploads that `check --sarif` run to code scanning. The spec contract carries its own analyzer, which checks a spec's string literals, and a completion provider, which offers your projects' namespace and type names as you type. The fluent surface can still move; [GRAMMAR.md](https://github.com/andypgray/loadbearing/blob/main/GRAMMAR.md) is its spec.

The six packages version in lockstep under 0.x semver. Every spec-API change is listed under Breaking in the [changelog](https://github.com/andypgray/loadbearing/blob/main/CHANGELOG.md) and ships without a shim, and a patch version makes none. The JSON documents carry a `schemaVersion` that moves when their shape does, and the human-readable reports can gain lines in any release. The public surface of the two packages a consumer compiles against is pinned by tests and compared with the previous release at pack time, so nothing there moves by accident.

## License

[MIT](https://github.com/andypgray/loadbearing/blob/main/LICENSE)

