SOC 2 Repository Structure: Organize the Program, Not Just Evidence
A practical SOC 2 repository structure for startups using JSON, Markdown, Git, stable record IDs, and separate paths for evidence and audit output.

A useful SOC 2 repository structure mirrors the program, not the auditor’s request list. Keep scope, systems, requirements, controls, owners, schedules, and evidence metadata as connected records. Keep policies and other long-form work beside those records. Let Git preserve each change, and generate audit delivery files from the source instead of treating a folder of uploads as the program.
TL;DR
- Give each record a stable ID and one authoritative file location.
- Store facts that software must validate in JSON and long-form work in Markdown.
- Link requirements, controls, policies, systems, recurring work, and evidence by ID instead of copying files into several criterion folders.
- Keep source data in the systems that produced it. Store or reference a fixed artifact only after checking access, retention, deletion, and sensitivity.
- Put lifecycle state and domain dates in records. Do not use Git branches as approval state or Git timestamps as the date an event happened.
- Treat audit packets and readiness views as derived output, not source files.
What is a SOC 2 repository structure?
A SOC 2 repository structure is the layout and set of rules used to store the records that describe and operate a SOC 2 program. It should answer five basic questions without forcing someone to search through ad hoc folders:
- What service, systems, people, and commitments are in scope?
- Which requirements apply, and which controls address them?
- Which policies, plans, and procedures govern the work?
- What work happened, when did it happen, and what evidence supports it?
- Which bounded records and artifacts should management give the CPA firm?
The AICPA Trust Services Criteria provide criteria used in a SOC 2 examination. They do not prescribe a folder tree for your company. The layout below is a FileGRC recommendation for teams that want an inspectable, machine-readable program.
Do not make audit request folders the source of truth
A first-time team often starts with folders named after criteria, control numbers, or requests from a readiness consultant. That can work for delivery, but it breaks down as an operating model.
One access review may support several controls and requirements. One policy may govern several systems. One control may need evidence from an identity provider, source control, and a ticket system. If you copy the same file into every matching folder, you soon have several versions with no clear owner.
Keep each source record once, then connect it to other records by stable ID. Generate the criterion matrix, request index, and delivery packet from those relationships. This makes the difference between a repository that stores uploads and one that can answer questions about the program.
A practical SOC 2 repository tree
A FileGRC workspace starts small and adds typed directories as the program grows. A mature workspace may look like this:
company-grc/
├── .gitignore
├── AGENTS.md
├── data/
│ ├── AGENTS.md
│ ├── renderer.json
│ ├── workspace.json
│ ├── programs/
│ ├── people/
│ ├── appointments/
│ ├── systems/
│ ├── components/
│ ├── frameworks/
│ ├── requirements/
│ ├── controls/
│ ├── policies/
│ ├── documents/
│ ├── risks/
│ ├── obligations/
│ ├── obligation-occurrences/
│ ├── evidence/
│ ├── audits/
│ ├── audit-populations/
│ └── audit-requests/
├── package.json
├── package-lock.json
└── README.md
The directories group records by type. Relationships between records explain how the program fits together. For example, a control can link to the systems where it applies, the requirements it addresses, the policy that governs it, and the components that produce evidence.
The root files have separate jobs:
| Path | Job |
|---|---|
.gitignore |
Keeps derived output and local dependency files out of Git |
AGENTS.md |
Gives people and agents root operating rules for the workspace |
data/AGENTS.md |
Gives record-specific creation, review, and validation rules |
data/renderer.json |
Sets repository mode, authoritative branch, and remote |
data/ |
Holds the source records and approved local artifacts |
package.json |
Defines workspace scripts and the allowed FileGRC dependency range |
package-lock.json |
Locks the installed FileGRC version and dependencies |
README.md |
Explains the company-specific workspace and how to use it |
Use a dedicated private repository unless a broader repository has access, review, retention, and history rules that fit the compliance records. A private repository still needs authentication, authorization, branch protection, backup, and recovery controls.
Put machine-readable facts in JSON
Store values that software must validate, filter, schedule, or connect in structured records. That includes:
- stable IDs and record types;
- owners, approvers, and reviewers;
- lifecycle status and domain dates;
- links to systems, requirements, controls, and evidence;
- recurring schedules and coverage periods;
- source, collection, verification, and retention facts.
A planned control record might look like this:
{
"id": "control-quarterly-access-review",
"type": "control",
"title": "Quarterly access review",
"status": "planned",
"statement": "Management reviews production access each quarter.",
"activity": "Review the complete access population and resolve exceptions.",
"operationMode": "manual",
"operationPattern": "scheduled",
"ownerIds": ["appointment-security-owner"],
"systemIds": ["system-production"],
"requirementIds": ["requirement-cc6-2"],
"policyIds": ["policy-information-security"]
}
The values must describe your real program. Starter records are proposals, not claims that a control exists or operates.
Put long-form work in Markdown beside its record
Policies, procedures, plans, minutes, and narratives need headings, paragraphs, tables, and links. Markdown keeps that work easy to review without forcing it into deeply nested JSON.
Use the same basename so a reader and a tool can pair the files:
data/policies/policy-information-security.json
data/policies/policy-information-security.md
The JSON record can hold the status, owners, approvers, dates, and approved content revision. A linked Obligation records the review schedule. The Markdown file holds the policy text. Any Markdown edit changes the reviewed revision, so approved content must go through the approval workflow again.
Keep evidence records connected to fixed artifacts
An evidence folder needs more than screenshots and exports. Each item should have a record that says what it is, where it came from, which period it covers, who collected it, who checked it, and which controls or operating records use it.
FileGRC keeps an evidence record and its permitted local attachments together:
data/evidence/evidence-access-review-2026-q3/
├── evidence.json
└── access-review-2026-q3.csv
Do not turn Git into a copy of every source system. Identity, cloud, monitoring, endpoint, backup, training, signature, procurement, and other systems still operate controls and hold their native data. Collect a fixed artifact when you need one. If the file contains restricted material or data that may need erasure, keep it in an appropriate controlled system and store a safe reference in the evidence record.
Never commit passwords, tokens, private keys, recovery codes, session material,
or live credentials as evidence. A .gitignore rule does not remove a file that
Git already tracks. GitHub’s guidance on
removing sensitive data from a repository
also warns that rewritten history does not remove every copy from clones,
forks, cached views, or pull requests.
The SOC 2 evidence repository guide covers the storage decision in more detail.
Record state belongs in the record, not the branch name
Draft, proposed, approved, active, superseded, and retired records can all live on the authoritative branch. Store the actual lifecycle status, approver, approval date, effective date, and approved content revision in the record.
A pull request can document review of a proposed change. A merge only proves that the repository changed. It does not prove that management approved a policy, that someone performed an access review, or that a control operated on the commit date.
Git history and domain records answer different questions:
| Question | Source |
|---|---|
| Who committed this file change? | Git commit |
| What changed between revisions? | Git diff |
| When did management approve the policy? | Policy record |
| When did the access review occur? | Operating record |
| What period does the artifact cover? | Evidence record |
Keep one authoritative branch for browser edits, protect it against force pushes and deletion, and use explicit record state for program decisions.
Keep generated output out of the source model
Readiness views, open-work lists, matrices, and audit packets should come from the current records. Do not hand-edit a generated checklist until it looks complete, because the source facts would still be wrong.
FileGRC writes evidence packets under .filegrc/evidence-packets/. That output
is derived, so it should not be edited or committed. A delivery-ready packet
means FileGRC’s management checks passed for the bounded set. The CPA firm still
decides whether the evidence is sufficient and appropriate and performs the
SOC 2 examination.
Make the structure usable by people, tools, and agents
A directory tree alone does not make a safe agent workflow. Give every writer the same model and operating rules, then require narrow changes and validation.
In a FileGRC workspace, an agent or engineer can ask for the next program step, inspect the model for a record type, and validate the result:
npx filegrc program-path --next --json
npx filegrc guide control --json
npm run validate
The guide reports required fields, valid values, relationship candidates, Markdown slots, and exact paths. That lets an agent work from the current model instead of guessing a schema from nearby files. People still supply and review the facts, approve governed content, operate controls, and decide whether a change should land.
The broader SOC 2 in Git guide explains the Git-native model. The compliance-as-code workflow connects these files to schedules, readiness checks, CI, and audit preparation.
How to set up the repository before you start SOC 2
Use this order before you fill the workspace with policies and evidence:
- Create a dedicated private repository and set access, branch protection, backup, recovery, and retention rules.
- Record the workspace, program, service, systems, components, people, and accountable appointments.
- Add the applicable framework requirements and connect them to controls.
- Add policy and document records, keeping long-form content in Markdown.
- Define recurring and event-driven obligations before the first evidence period begins.
- Identify each control’s source components and decide where fixed artifacts may be stored safely.
- Validate every change and review the full diff before it reaches the authoritative branch.
- Generate audit views and packets from the source records when management and the CPA firm need them.
This order gives you a connected program before the first batch of screenshots arrives. It also makes later questions easier to answer because each evidence item has a source, each control has an owner, and each requirement points to the work that addresses it.
You can create the open source workspace with:
npx create-filegrc@latest
FileGRC is a Git-native GRC workspace for SOC 2 work. JSON holds structured records, Markdown holds long-form work, and Git supplies the change history. It does not replace your source systems or the independent CPA firm.
Run your SOC 2 program as files in Git.
Keep policies, controls, work, and evidence indexes in a repository your team and agents can inspect.
Frequently asked questions
What is a good SOC 2 repository structure?
A good SOC 2 repository separates structured program records, long-form content, operating work, evidence metadata, approved fixed artifacts, and derived audit output. It uses stable record IDs and relationships so one control, policy, or evidence item can support more than one requirement without being copied into several folders.
Should a SOC 2 repository be organized by Trust Services Criteria?
Use criteria as requirements that link to controls, not as the top-level home for every file. A control or evidence item may support several criteria, so criterion folders tend to create copies and conflicting versions.
Should SOC 2 evidence go in the same Git repository?
Some approved fixed artifacts can go in a private Git repository when access, retention, deletion, size, and backup rules fit the data. Keep secrets, credentials, high-risk personal data, and material that may need erasure in an appropriate restricted system, then store a safe reference in the program record.
Should policy approval use Git branches or pull requests?
Use an explicit lifecycle status, approver, approval date, and approved content revision in the policy record. A pull request can support review, but a branch name or merged change does not by itself prove that management approved the policy.
What belongs in JSON and what belongs in Markdown?
Put IDs, owners, statuses, dates, relationships, schedules, and other values that software must validate in JSON. Put policies, procedures, plans, minutes, and narratives in Markdown beside the related JSON record.
Does FileGRC create a SOC 2 report?
No. FileGRC organizes management's program records and can prepare a bounded evidence packet. An independent CPA firm performs the SOC 2 examination, judges the evidence, and issues the SOC 2 report.