Note 03 · Hard controls · Composite company

Your CUI boundary includes the build path

Check the repo host's FedRAMP tier, lock one ephemeral runner group to the defense repo, then run a canary string through the pipeline to find the leaks.

The repository is one stop. Repository. Runner and workspace. Logs, caches, artifacts. Worked design.
In this field note

Updated September 29, 2026

If you run a software company with one defense contract, this is the decision that sets your CMMC bill. Your whole build system gets examined only if you let the defense project's sensitive data spread through it. Give that project its own code host and its own small set of build machines, then prove the data stays put with a test string before you load anything real.

The sensitive data is CUI (controlled unclassified information): specs, test data, and other technical material the government marks as sensitive. Anything that stores or touches it lands in your scope, the set of systems the assessor examines under CMMC, the Defense Department's cybersecurity program for suppliers. Everything in scope has to meet all 110 of its security requirements. The build path is the chain of servers that turns your developers' code into a product: the code host, the build machines (called runners), and the logs, caches, and packaged files they leave behind.

The team in this example has twenty developers building a commercial product. Four of them work on a defense project whose customer sent an interface spec and test data marked CUI. Their first instinct was to add the defense code to their existing GitHub account and let it build on the shared build machines. That would have put every build machine, log store, and cache they own inside the assessment, and every one of them would need all 110 requirements, the same trap as putting the whole company in scope when only one team needs it. Separating it cost them an afternoon.

The rest of this post is the engineering. Hand it to whoever runs your builds; it's written for them.

Start with where the code lives

Under DFARS 7012, a cloud service that stores CUI has to meet FedRAMP Moderate (the government's cloud security approval) or equivalent. Look up the exact offering on the FedRAMP Marketplace, down to the product tier, because commercial and government editions of the same product are often authorized differently (more on equivalency). If your host doesn't show up at Moderate or higher, the simplest fix is a self-hosted GitHub Enterprise Server or GitLab Self-Managed inside the CUI enclave (on a FedRAMP-authorized cloud such as AWS GovCloud, or on your own hardware), the walled-off environment where CUI work happens, used for this one repo.

Keep the real test data out of daily work, too. The four developers write against synthetic fixtures and interface stubs. Only the integration job loads the customer's data, and that job only runs inside the enclave.

Lock down the runners

In GitHub Actions this takes about twenty minutes. Under Org Settings → Actions → Runner groups, create a group, set Repository access to Selected repositories, and tick only the defense repo. The default lets every repo in the org schedule onto every org-level group, which is how a harmless side project ends up running on your CUI runner (runner groups).

Register the runners with --ephemeral. Each one takes a single job and deregisters. Deregistering doesn't wipe the disk, though. GitHub leaves the wipe to your own automation, so rebuild the VM from a clean image after every job (self-hosted runners). Keep the repo private; GitHub advises against self-hosted runners on public repos because a fork's pull request can run code on your machine (secure use). Finally, limit the runner's outbound traffic to the repo host, your package mirror, and the artifact store.

Run the canary

A canary is a made-up marker string you plant in the test data and then search for everywhere, to see where the data leaked. Put SCOPE-PILOT-104 in one fixture file. Run a passing build, then a build with a deliberately failing assertion. Grep everything the pipeline produced for the string: console logs, test reports, uploaded artifacts, cache, and the runner's disk after the job finishes.

The first run on this team's pipeline turned up three hits. The failing assertion printed the fixture into the hosted build log, which was outside the enclave. The artifact step had zipped the entire workspace, fixtures included. And a job from an unrelated repo had landed on the runner, because the group was still set to All repositories. They masked test output and kept logs in the enclave. They replaced the zip step with an explicit file list and fixed the group setting. The second run found the string only where it belonged. Record the build IDs of both runs; they're your evidence for 3.1.3, the requirement to control where CUI flows.

If a separate team owns the org-level runner settings, bring them in before any of this. Those org admin rights are an admin path that can pull systems into scope on their own. A developer can't fix runner access by editing a workflow file, however many times they push.

What the owner gets at the end

A list of in-scope equipment short enough to write on an index card, which is what a good small-company scope looks like: four developer laptops, the code host, and the build machines with the process that rebuilds them. The other sixteen developers and the commercial pipeline stay out of the assessment. Download the build-path worksheet to record it. The data-flow guide covers everything upstream of the repo.

Mock assessment

Turn the canary results into your SSP.

Garde1 carries your repo host and its protections into your scope, policies and mock assessment, and lists what's still missing.

Or start a 14-day trial

HOSTED ON FEDRAMP MODERATE AWS · ITAR-AWARE