create-filegrc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/bin/create-filegrc.js +8 -0
  4. package/package.json +26 -0
  5. package/src/cli.js +64 -0
  6. package/src/defaults.js +1063 -0
  7. package/src/index.js +252 -0
  8. package/template/AGENTS.md +227 -0
  9. package/template/README.md +102 -0
  10. package/template/data/AGENTS.md +185 -0
  11. package/template/data/action-items/AGENTS.md +11 -0
  12. package/template/data/audit-populations/AGENTS.md +13 -0
  13. package/template/data/audits/AGENTS.md +21 -0
  14. package/template/data/documents/document-business-continuity-disaster-recovery.json +29 -0
  15. package/template/data/documents/document-business-continuity-disaster-recovery.md +190 -0
  16. package/template/data/documents/document-contractor-policy-acknowledgement.json +21 -0
  17. package/template/data/documents/document-contractor-policy-acknowledgement.md +24 -0
  18. package/template/data/documents/document-contractor-training-acknowledgement.json +22 -0
  19. package/template/data/documents/document-contractor-training-acknowledgement.md +20 -0
  20. package/template/data/documents/document-data-retention-schedule.json +25 -0
  21. package/template/data/documents/document-data-retention-schedule.md +33 -0
  22. package/template/data/documents/document-employee-handbook-acknowledgement.json +21 -0
  23. package/template/data/documents/document-employee-handbook-acknowledgement.md +19 -0
  24. package/template/data/documents/document-employee-policy-acknowledgement.json +21 -0
  25. package/template/data/documents/document-employee-policy-acknowledgement.md +24 -0
  26. package/template/data/documents/document-employee-training-acknowledgement.json +22 -0
  27. package/template/data/documents/document-employee-training-acknowledgement.md +20 -0
  28. package/template/data/documents/document-incident-response-plan.json +29 -0
  29. package/template/data/documents/document-incident-response-plan.md +136 -0
  30. package/template/data/documents/document-soc2-management-assertion.json +17 -0
  31. package/template/data/documents/document-soc2-management-assertion.md +24 -0
  32. package/template/data/documents/document-soc2-management-representation.json +17 -0
  33. package/template/data/documents/document-soc2-management-representation.md +20 -0
  34. package/template/data/documents/document-soc2-period-completeness.json +17 -0
  35. package/template/data/documents/document-soc2-period-completeness.md +32 -0
  36. package/template/data/documents/document-soc2-system-description.json +17 -0
  37. package/template/data/documents/document-soc2-system-description.md +65 -0
  38. package/template/data/evidence/AGENTS.md +30 -0
  39. package/template/data/obligation-events/AGENTS.md +18 -0
  40. package/template/data/obligations/AGENTS.md +11 -0
  41. package/template/data/people/person-independent-approver.json +10 -0
  42. package/template/data/people/person-policy-owner.json +10 -0
  43. package/template/data/policies/AGENTS.md +15 -0
  44. package/template/data/policies/policy-anti-bribery-corruption.json +25 -0
  45. package/template/data/policies/policy-anti-bribery-corruption.md +87 -0
  46. package/template/data/policies/policy-clear-desk-screen.json +24 -0
  47. package/template/data/policies/policy-clear-desk-screen.md +49 -0
  48. package/template/data/policies/policy-data-protection-handling.json +34 -0
  49. package/template/data/policies/policy-data-protection-handling.md +130 -0
  50. package/template/data/policies/policy-employee-handbook.json +31 -0
  51. package/template/data/policies/policy-employee-handbook.md +161 -0
  52. package/template/data/policies/policy-information-security.json +61 -0
  53. package/template/data/policies/policy-information-security.md +233 -0
  54. package/template/data/policies/policy-mobile-computing-communications.json +28 -0
  55. package/template/data/policies/policy-mobile-computing-communications.md +74 -0
  56. package/template/data/renderer.json +7 -0
  57. package/template/data/risk-assessments/AGENTS.md +16 -0
  58. package/template/data/systems/system-filegrc-program-repository.md +9 -0
  59. package/template/data/training/training-anti-bribery-high-risk-roles.json +17 -0
  60. package/template/data/training/training-anti-bribery-high-risk-roles.md +19 -0
  61. package/template/data/training/training-privileged-sensitive-roles.json +21 -0
  62. package/template/data/training/training-privileged-sensitive-roles.md +20 -0
  63. package/template/data/training/training-secure-development.json +20 -0
  64. package/template/data/training/training-secure-development.md +22 -0
  65. package/template/data/training/training-security-awareness.json +28 -0
  66. package/template/data/training/training-security-awareness.md +192 -0
  67. package/template/data/workspace.json +27 -0
  68. package/template/docs/filegrc-audit.png +0 -0
  69. package/template/docs/filegrc-home.png +0 -0
  70. package/template/gitignore +4 -0
  71. package/template/package.json +15 -0
  72. package/template-parameters.json +34 -0
package/src/index.js ADDED
@@ -0,0 +1,252 @@
1
+ import { execFile } from "node:child_process";
2
+ import { cp, lstat, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, extname, join, relative, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { promisify } from "node:util";
6
+ import { createInterface } from "node:readline/promises";
7
+ import { baselineRecordPaths, writeBaselineRecords } from "./defaults.js";
8
+
9
+ const execute = promisify(execFile);
10
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
11
+ const textExtensions = new Set(["", ".json", ".md", ".txt", ".yml", ".yaml", ".gitignore"]);
12
+
13
+ export async function createFileGRC(options = {}) {
14
+ const parameterConfig = JSON.parse(await readFile(join(packageRoot, "template-parameters.json"), "utf8"));
15
+ const target = resolve(options.target ?? "filegrc-program");
16
+ await assertWritableTarget(target, Boolean(options.force));
17
+ if (options.force) await assertNoTemplateCollisions(target);
18
+
19
+ const prompted = await resolvePromptValues(parameterConfig.parameters, options);
20
+ const engineVersion = await resolveFileGRCVersion(options.filegrcVersion);
21
+ const values = {
22
+ ...prompted,
23
+ effective_date: options.effectiveDate ?? new Date().toISOString().slice(0, 10),
24
+ project_name: normalizePackageName(basename(target)),
25
+ filegrc_version_range: `^${engineVersion}`
26
+ };
27
+
28
+ await mkdir(target, { recursive: true });
29
+ await copyTemplate(target);
30
+ await renderTemplate(target, parameterConfig, values);
31
+ await writeBaselineRecords(target, values.effective_date);
32
+
33
+ if (options.install !== false) {
34
+ await run("npm", ["install", "--ignore-scripts"], target);
35
+ } else {
36
+ await writeMinimalLockfile(target, values.project_name, values.filegrc_version_range);
37
+ }
38
+ if (!await isInsideGitWorktree(target)) await run("git", ["init"], target);
39
+ return { target, values, engineVersion };
40
+ }
41
+
42
+ export async function resolveFileGRCVersion(explicitVersion) {
43
+ if (explicitVersion) return cleanVersion(explicitVersion);
44
+ try {
45
+ const { stdout } = await execute("npm", ["view", "filegrc", "version", "--json"], {
46
+ timeout: 15_000,
47
+ maxBuffer: 100_000
48
+ });
49
+ const parsed = JSON.parse(stdout);
50
+ return cleanVersion(Array.isArray(parsed) ? parsed.at(-1) : parsed);
51
+ } catch {
52
+ const ownPackage = JSON.parse(await readFile(join(packageRoot, "package.json"), "utf8"));
53
+ return cleanVersion(ownPackage.version);
54
+ }
55
+ }
56
+
57
+ async function resolvePromptValues(parameters, options) {
58
+ const mapped = {
59
+ company_name: options.companyName,
60
+ policy_owner_name: options.policyOwnerName,
61
+ security_contact_email: options.securityContactEmail
62
+ };
63
+ if (options.yes) {
64
+ mapped.company_name ??= "Example Company";
65
+ mapped.policy_owner_name ??= "Security Owner";
66
+ mapped.security_contact_email ??= "security@example.com";
67
+ }
68
+ for (const key of Object.keys(mapped)) {
69
+ if (mapped[key] !== undefined && mapped[key] !== null) mapped[key] = String(mapped[key]).trim();
70
+ }
71
+ const missing = parameters.filter(({ key, required }) => required && !mapped[key]);
72
+ if (missing.length) {
73
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
74
+ throw new Error(`Missing required values: ${missing.map(({ key }) => key).join(", ")}`);
75
+ }
76
+ const prompt = createInterface({ input: process.stdin, output: process.stdout });
77
+ try {
78
+ for (const parameter of missing) {
79
+ let value = "";
80
+ while (!value) value = (await prompt.question(`${parameter.prompt}: `)).trim();
81
+ mapped[parameter.key] = value;
82
+ }
83
+ } finally {
84
+ prompt.close();
85
+ }
86
+ }
87
+ for (const key of ["company_name", "policy_owner_name"]) {
88
+ if (/[\u0000-\u001f\u007f]/.test(mapped[key])) {
89
+ throw new Error(`${key} must be a single line without control characters.`);
90
+ }
91
+ if (mapped[key].length > 200) throw new Error(`${key} must be 200 characters or fewer.`);
92
+ if (/\{\{[a-z0-9_]+\}\}/.test(mapped[key])) {
93
+ throw new Error(`${key} cannot contain template token syntax.`);
94
+ }
95
+ }
96
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(mapped.security_contact_email)) {
97
+ throw new Error("Security contact email must be a valid email address.");
98
+ }
99
+ return mapped;
100
+ }
101
+
102
+ async function assertWritableTarget(target, force) {
103
+ try {
104
+ if ((await lstat(target)).isSymbolicLink()) {
105
+ throw new Error(`Target directory must not be a symbolic link: ${target}.`);
106
+ }
107
+ const items = await readdir(target);
108
+ if (items.length && !force) {
109
+ throw new Error(`Target directory is not empty: ${target}. Pass --force to add files without overwriting existing paths.`);
110
+ }
111
+ } catch (error) {
112
+ if (error.code !== "ENOENT") throw error;
113
+ }
114
+ }
115
+
116
+ async function assertNoTemplateCollisions(target) {
117
+ const collisions = [];
118
+ for (const destinationPath of [...await templateDestinationPaths(), ...baselineRecordPaths()]) {
119
+ await assertNoSymlinkComponents(target, destinationPath);
120
+ try {
121
+ await lstat(join(target, destinationPath));
122
+ collisions.push(destinationPath);
123
+ } catch (error) {
124
+ if (error.code !== "ENOENT") throw error;
125
+ }
126
+ }
127
+ if (collisions.length) {
128
+ throw new Error(`Target contains files create-filegrc would overwrite: ${collisions.slice(0, 5).join(", ")}.`);
129
+ }
130
+ }
131
+
132
+ async function assertNoSymlinkComponents(target, relativePath) {
133
+ let current = target;
134
+ for (const segment of relativePath.split(/[\\/]/).filter(Boolean)) {
135
+ current = join(current, segment);
136
+ try {
137
+ if ((await lstat(current)).isSymbolicLink()) {
138
+ throw new Error(`Target contains a symbolic link at ${relative(target, current)}.`);
139
+ }
140
+ } catch (error) {
141
+ if (error.code === "ENOENT") return;
142
+ throw error;
143
+ }
144
+ }
145
+ }
146
+
147
+ async function renderTemplate(target, parameterConfig, values) {
148
+ const declared = new Set([
149
+ ...parameterConfig.parameters.map(({ key }) => key),
150
+ ...parameterConfig.generated.map(({ key }) => key)
151
+ ]);
152
+ const files = (await templateDestinationPaths()).map((path) => join(target, path));
153
+ for (const path of files) {
154
+ if (!textExtensions.has(extname(path)) && basename(path) !== ".gitignore") continue;
155
+ let source = await readFile(path, "utf8");
156
+ const jsonFile = extname(path) === ".json";
157
+ source = source.replace(/\{\{([a-z0-9_]+)\}\}/g, (match, token) => {
158
+ if (!declared.has(token)) throw new Error(`Unknown template token "{{${token}}}" in ${path}`);
159
+ if (values[token] === undefined) throw new Error(`No value resolved for template token "{{${token}}}"`);
160
+ return jsonFile ? jsonStringContents(values[token]) : String(values[token]);
161
+ });
162
+ const unresolved = /\{\{([a-z0-9_]+)\}\}/.exec(source);
163
+ if (unresolved) throw new Error(`Unresolved template token "{{${unresolved[1]}}}" in ${path}`);
164
+ await writeFile(path, source, "utf8");
165
+ }
166
+ }
167
+
168
+ async function templateDestinationPaths() {
169
+ const template = join(packageRoot, "template");
170
+ return (await collectFiles(template)).map((source) => {
171
+ const templatePath = relative(template, source);
172
+ return templatePath === "gitignore" ? ".gitignore" : templatePath;
173
+ });
174
+ }
175
+
176
+ async function copyTemplate(target) {
177
+ const template = join(packageRoot, "template");
178
+ for (const source of await collectFiles(template)) {
179
+ const templatePath = relative(template, source);
180
+ const destinationPath = templatePath === "gitignore" ? ".gitignore" : templatePath;
181
+ const destination = join(target, destinationPath);
182
+ await assertNoSymlinkComponents(target, destinationPath);
183
+ await mkdir(dirname(destination), { recursive: true });
184
+ await cp(source, destination, {
185
+ force: false,
186
+ errorOnExist: true,
187
+ preserveTimestamps: false
188
+ });
189
+ }
190
+ }
191
+
192
+ async function collectFiles(directory) {
193
+ const result = [];
194
+ for (const item of await readdir(directory, { withFileTypes: true })) {
195
+ const path = join(directory, item.name);
196
+ if (item.isDirectory()) result.push(...await collectFiles(path));
197
+ else if (item.isFile()) result.push(path);
198
+ }
199
+ return result;
200
+ }
201
+
202
+ async function writeMinimalLockfile(target, name, versionRange) {
203
+ const lock = {
204
+ name,
205
+ version: "0.1.0",
206
+ lockfileVersion: 3,
207
+ requires: true,
208
+ packages: {
209
+ "": {
210
+ name,
211
+ version: "0.1.0",
212
+ dependencies: { filegrc: versionRange }
213
+ }
214
+ }
215
+ };
216
+ await writeFile(join(target, "package-lock.json"), `${JSON.stringify(lock, null, 2)}\n`, "utf8");
217
+ }
218
+
219
+ async function isInsideGitWorktree(target) {
220
+ try {
221
+ await execute("git", ["rev-parse", "--is-inside-work-tree"], { cwd: target });
222
+ return true;
223
+ } catch {
224
+ return false;
225
+ }
226
+ }
227
+
228
+ async function run(command, args, cwd) {
229
+ try {
230
+ return await execute(command, args, { cwd, maxBuffer: 10_000_000 });
231
+ } catch (error) {
232
+ const detail = error.stderr?.trim() || error.stdout?.trim() || error.message;
233
+ throw new Error(`${command} ${args.join(" ")} failed: ${detail}`);
234
+ }
235
+ }
236
+
237
+ function cleanVersion(value) {
238
+ const version = String(value ?? "").trim().replace(/^v/, "");
239
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
240
+ throw new Error(`Could not resolve a valid FileGRC version from "${value}".`);
241
+ }
242
+ return version;
243
+ }
244
+
245
+ function normalizePackageName(value) {
246
+ const normalized = value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
247
+ return normalized || "filegrc-program";
248
+ }
249
+
250
+ function jsonStringContents(value) {
251
+ return JSON.stringify(String(value)).slice(1, -1);
252
+ }
@@ -0,0 +1,227 @@
1
+ # FileGRC SOC 2 Workspace Instructions
2
+
3
+ ## Purpose
4
+
5
+ This repository is {{company_name}}’s FileGRC workspace for its SOC 2 program. Engineers and agents maintain the source records under `data/`. The `filegrc` package validates, searches, edits, and renders those files.
6
+
7
+ Using this repository does not establish compliance by itself. Records must match actual practice and evidence must prove that controls operated during the audit period.
8
+
9
+ ## Agent quick start
10
+
11
+ Do not guess a resource type, field name, enum value, relationship, or file path. Start every unfamiliar task with the installed model:
12
+
13
+ ```sh
14
+ npx filegrc guide --json
15
+ npx filegrc guide risk-assessment --json
16
+ npx filegrc list person --json
17
+ ```
18
+
19
+ The first command lists every supported action and record type. The type guide gives the purpose, policy basis, timing, required and conditional fields, current relationship candidates, JSON location, and Markdown slots.
20
+
21
+ For a new record, generate a mutation envelope:
22
+
23
+ ```sh
24
+ npx filegrc scaffold risk-assessment --title "2026 Annual Risk Assessment" > /tmp/risk-assessment.json
25
+ ```
26
+
27
+ The scaffold contains `{ "record": ..., "content": ... }`, which is the same payload shape used by the renderer. Null values and empty required arrays are deliberate prompts. Replace all of them with facts before creation:
28
+
29
+ ```sh
30
+ npx filegrc create /tmp/risk-assessment.json
31
+ npx filegrc validate --json
32
+ git diff --check
33
+ git diff
34
+ ```
35
+
36
+ Read `data/AGENTS.md` before changing records. More specific instructions inside high-risk collections apply in addition to that file.
37
+
38
+ ## Working rules
39
+
40
+ - Read `README.md` and run `npm run validate` before broad changes.
41
+ - Treat `data/` as the source of truth. Do not hand-edit `.filegrc/` output.
42
+ - Use UTF-8 JSON for structured records and Markdown for long-form work.
43
+ - Keep one resource in each JSON file.
44
+ - Let the local app generate IDs from each record’s name or title. When editing JSON directly, keep IDs globally unique, human-readable, and lowercase kebab-case.
45
+ - Use ISO 8601 dates and RFC 3339 timestamps.
46
+ - Store relationships as resource IDs.
47
+ - Put policies, plans, charters, procedures, meeting minutes, training, assertions, narratives, templates, and audit responses in Markdown beside their JSON records. FileGRC derives the Markdown name, so records do not contain file paths.
48
+ - Put signed forms, screenshots, third-party reports, and immutable exports behind evidence records. These files may be PDF, image, CSV, or another fixed format.
49
+ - Never fetch an external evidence reference automatically.
50
+ - Do not store secrets, credentials, session data, or personal data that may need to be erased from Git history.
51
+ - Keep the editable local server on loopback or behind trusted authentication. Use the read-only static build for audit sharing.
52
+
53
+ ## Git is the audit trail
54
+
55
+ Git supplies file authors, commit timestamps, messages, diffs, and revisions. Do not add fields such as `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, or a second change log.
56
+
57
+ Domain events still need explicit dates. Keep values such as `occurredOn`, `approvedOn`, `reviewedOn`, `completedOn`, and audit-period dates in their records.
58
+
59
+ Make focused commits with messages that explain the reason for the change. The engine never creates commits automatically. Review the workspace diff, then use the commit action on Repository or the Git CLI. The renderer validates the workspace and requires an explicit message before it creates a commit.
60
+
61
+ Pull before starting work when other people or agents may have changed the repository. Without a remote, the browser's Repository page creates a local commit and hides synchronization actions. With a remote, it pulls with rebase, refuses to pull over uncommitted files, and pushes immediately after it creates a commit. Agents and terminal users own Git synchronization and should run `git pull --rebase`, `git commit`, and `git push` directly. Do not create merge commits for routine synchronization.
62
+
63
+ Do not rewrite or remove committed records that explain prior audit periods. Close or retire them. Delete only mistakes and uncommitted drafts.
64
+
65
+ ## Data model
66
+
67
+ `data/workspace.json` selects the model through `dataModelVersion`. The installed `filegrc` package owns the authoritative model. Do not copy or invent a local schema.
68
+
69
+ Run these commands when working with records:
70
+
71
+ ```sh
72
+ npm run validate
73
+ npx filegrc guide --json
74
+ npx filegrc guide risk
75
+ npx filegrc list risk --json
76
+ npx filegrc get risk-example
77
+ npx filegrc get risk-example --mutation
78
+ npx filegrc references risk-example --json
79
+ npx filegrc describe risk
80
+ npx filegrc search "access review"
81
+ npm run serve
82
+ ```
83
+
84
+ Prefer existing fields. Put organization-specific values under `extensions` with a namespace owned by {{company_name}}. Add structure only when validation, filtering, relationships, due dates, or audit completeness need it. Variable procedures, interviews, observations, rationale, and detailed results belong in the record's Markdown companion.
85
+
86
+ Never change a resource ID after it is committed. Create a replacement and link the records if identity truly changes.
87
+
88
+ The local app keeps IDs out of the guided form, generates them during creation, and leaves them unchanged when a record is renamed. It presents the core model fields and relationship pickers. Use its advanced JSON section for optional fields and extensions. It rejects a save if the source file changed after the editor opened, so reload and reapply the change instead of overwriting newer work.
89
+
90
+ Headless agents get the same protection by exporting an edit payload with `filegrc get RESOURCE_ID --mutation` and passing that file to `filegrc update`.
91
+
92
+ ## Renderer settings and onboarding
93
+
94
+ `data/renderer.json` stores committed renderer preferences. New workspaces set `showOnboarding` to `true`. Completing or skipping onboarding sets it to `false`; the app does not commit that change.
95
+
96
+ Onboarding explains the file and Git workflow, recurring obligations, event checklists, and bulk evidence preparation, then collects the initial service boundary, owner, business criticality, highest data classification, internet exposure, and optional audit objective. It creates or updates a `system` record and may create a planned `audit` record. Treat both as drafts to review against actual scope.
97
+
98
+ The renderer is optional. Agents may set `showOnboarding` to `false` and maintain all records headlessly. Restart onboarding from Repository when useful. Read-only builds never run it.
99
+
100
+ ## Starter baseline
101
+
102
+ The generated workspace starts with the SOC 2 Security category:
103
+
104
+ - Active framework records for the 2017 Trust Services Criteria with revised points of focus (2022) and the 2018 SOC 2 Description Criteria with revised implementation guidance (2022)
105
+ - The 33 Common Criteria reference IDs from CC1.1 through CC9.2, without the licensed criteria text
106
+ - The nine Description Criteria reference IDs from DC1 through DC9, without the licensed criteria text
107
+ - Planned controls mapped to those references and the included policies
108
+ - A security and risk oversight team chaired by an external independent reviewer
109
+ - Recurring obligations for the reviews, scans, tests, training, and meetings required by the included policies
110
+ - A default 5x5 risk method and Public, Internal, Confidential, and Restricted data classifications
111
+
112
+ Treat every planned control as a proposal until its owner, scope, operation, and evidence match actual practice. Do not mark a control implemented because a policy describes it. Add Availability, Processing Integrity, Confidentiality, or Privacy criteria only when they are in scope.
113
+
114
+ The recurring obligations mirror the fixed cadences in the starter policies. Update the policy, control, and obligation together when an approved cadence changes. Create separate completion records, such as meetings, reviews, scans, tests, exercises, and attestations, for each period.
115
+
116
+ ## Policy work queue
117
+
118
+ Run the same obligation planner used by the web app:
119
+
120
+ ```sh
121
+ npx filegrc obligations --json
122
+ npx filegrc obligations --from 2026-01-01 --through 2026-12-31 --complete --json
123
+ ```
124
+
125
+ A calendar obligation’s recurrence anchor starts its first allowed cycle. Unless `window` narrows that range, completion is allowed from the cycle start through the day before the next cycle, and the item becomes overdue on the next cycle’s first day. Use **Record work** in the obligation board, or create and link a completion atomically with:
126
+
127
+ ```sh
128
+ npx filegrc complete obligation-id completion-record.json
129
+ ```
130
+
131
+ Keep prior completion links because the planner matches each dated record to its own period.
132
+
133
+ Event obligations are templates. Do not mark a template complete or replace it for each occurrence. Start a workflow in the obligation board or run:
134
+
135
+ ```sh
136
+ npx filegrc trigger person-started --occurred-on 2026-07-25 --subject person-new-worker --json
137
+ npx filegrc trigger person-ended --occurred-at 2026-07-25T16:30:00-05:00 --subject person-departing-worker --json
138
+ ```
139
+
140
+ The command creates one `obligation-event` and its complete action checklist in a single validated write. Hour-based deadlines require an RFC 3339 event timestamp so an immediate or 24-hour cutoff is exact. Day-based deadlines use the event’s calendar date. Link the requested completion resources and evidence to each action item, then mark the actions done and the event complete. Every generated action has a cutoff. FileGRC applies a 30-day deadline when a custom event obligation omits one.
141
+
142
+ Complete an event action and link its new proof in one validated write:
143
+
144
+ ```sh
145
+ npx filegrc complete-action action-item-id completion-record.json --completed-on 2026-07-25
146
+ npx filegrc complete-event obligation-event-id --completed-on 2026-07-25
147
+ ```
148
+
149
+ FileGRC rejects a completion resource whose type does not match the obligation. It will close the event only after every action has its requested proof.
150
+
151
+ ## Headless Markdown
152
+
153
+ Create and update JSON plus Markdown in one mutation envelope when practical. You can also inspect or replace a companion directly:
154
+
155
+ ```sh
156
+ npx filegrc content risk-assessment risk-assessment-2026 --json
157
+ npx filegrc content risk-assessment risk-assessment-2026 --write updated-assessment.md
158
+ ```
159
+
160
+ Run `filegrc guide <type>` to get slot names. Policies use `content`, meetings use `agenda` and `minutes`, and implicit long-form work uses `record`. FileGRC derives the path and rejects content that does not belong to the record.
161
+
162
+ ## Audit preparation and evidence packets
163
+
164
+ After setting a Type 1 as-of date or Type 2 period, initialize the engagement-specific management work:
165
+
166
+ ```sh
167
+ npx filegrc prepare-audit audit-2026-type-2
168
+ npx filegrc audit-readiness audit-2026-type-2
169
+ npx filegrc audit-readiness audit-2026-type-2 --require-ready --json
170
+ ```
171
+
172
+ Preparation creates a separate system description, management assertion, and management representation document for the engagement from the local starter templates. Type 2 preparation also creates a period completeness statement and one `audit-population` record for each standard population. It is safe to run again and does not approve documents, mark controls implemented, or create evidence. Do not reuse one completed management document across engagements.
173
+
174
+ Near the end of fieldwork, link a verified fixed-format copy of the signed management representation letter to its engagement-specific document. Date it on or after the Type 1 date or Type 2 period end. A representation that is still marked for later blocks packet delivery.
175
+
176
+ Catalog each authoritative source under Systems and assign its `evidenceSourceKinds`. Name the people who can access its reports and keep extraction instructions in the system's Record Markdown. For each Type 2 population, select one source system and export the exact audit period. Split a population when different systems or queries produce its items. Link a verified `population-export` evidence record that names the same source system and stores the query or report parameters, generation time, timezone, count, completeness check, and accuracy check. A zero count still requires the source export and query. A population linked to an in-scope control cannot be marked not applicable.
177
+
178
+ Every evidence record names its collector. Verified evidence also names its verifier and verification date. Use `sourceSystemId` for system exports, `sourceResourceIds` for FileGRC records, and `sourceCommit` to bind the evidence to repository state.
179
+
180
+ Preview coverage before writing output:
181
+
182
+ ```sh
183
+ npx filegrc evidence-packet --audit audit-2026-type-2 --preview --json
184
+ npx filegrc evidence-packet --audit audit-2026-type-2
185
+ npx filegrc evidence-packet --audit audit-2026-type-2 --preview --require-ready
186
+ ```
187
+
188
+ The packet includes records explicitly related to the selected engagement, its systems, controls, criteria, policies, evidence, and dependencies. It does not include unrelated dated records from the workspace. A Type 2 packet adds period operating records, recurring obligation occurrences, event workflows, and management population reconciliations. Output includes a control matrix, source-system index, external-evidence delivery index, population index, evidence index, committed historical source versions, and SHA-256 checksums. Output under `.filegrc/evidence-packets/` is derived and must not be hand-edited or committed.
189
+
190
+ Treat a packet as ready for management delivery only when its status is `delivery-ready`, its review list is clear, and its manifest names a clean Git revision. This means FileGRC's management checks passed. It does not mean the engagement team found the evidence sufficient or appropriate. The generator copies raw records, Markdown, and local fixed attachments. It never fetches external references. Reconcile `external-evidence-index.csv` to the auditor portal or other approved delivery system before telling the engagement team that submission is complete.
191
+
192
+ Link a control test to its `audit-population` record when sampling applies. Link item-level sample evidence separately. Management owns population completeness and accuracy. The auditor owns sample selection, independent testing, exception evaluation, and the report opinion. The auditor or publisher also supplies the authoritative criteria and examination guidance. FileGRC stores references and orientation text, not licensed criteria.
193
+
194
+ ## Content and approvals
195
+
196
+ The seed policy owner is {{policy_owner_name}} and the reporting address is {{security_contact_email}}. Replace ownership or contacts when responsibilities change.
197
+
198
+ The starter requires an external independent reviewer for SOC 2 oversight. This person must be separate from the policy owner and must not operate the controls they review. They chair Security and Risk Oversight and approve policies and governed documents. A one-person company must appoint a qualified person outside the company before approving the program. Do not use the audit firm for management or approval work without first confirming the firm's independence requirements.
199
+
200
+ Policy and training attestations must identify the exact Git revision of the content that a person acknowledged. Store signatures as evidence attachments and link their evidence IDs from the attestation.
201
+
202
+ Committee and risk meeting minutes are `meeting` resources with a primary Markdown companion. An optional `-agenda.md` companion holds the agenda. Record attendees, decisions, risks discussed, and action item IDs. Keep action tracking in `action-item` records.
203
+
204
+ ## Audit evidence
205
+
206
+ A rendered-page evidence capture must identify:
207
+
208
+ - The route and filters shown
209
+ - The audit or evidence period
210
+ - The exact Git commit
211
+ - The capture timestamp and method
212
+ - The source resource IDs
213
+ - The screenshot or fixed export
214
+
215
+ Commit the evidence record and attachment together. Do not claim that a current page proves a prior period unless it is rendered from or bound to the correct revision.
216
+
217
+ ## Validation
218
+
219
+ After substantive edits:
220
+
221
+ 1. Run `npm run validate`.
222
+ 2. Review JSON and Markdown diffs.
223
+ 3. Check every new relationship and attachment.
224
+ 4. Confirm dates describe the business event, not the edit time.
225
+ 5. Run `npm run build` when producing a read-only audit view.
226
+
227
+ Do not loosen validation to make a bad record pass. Fix the record or update the installed engine through its normal versioned model process.
@@ -0,0 +1,102 @@
1
+ # FileGRC
2
+
3
+ Run a SOC 2 program as files in Git.
4
+
5
+ FileGRC gives a founder-led engineering team one place to adopt policies, track recurring compliance work, respond to company events, and prepare an audit. JSON holds structured records, Markdown holds long-form work, and Git supplies the change history.
6
+
7
+ There is no separate application database. The repository is the program, so engineers and agents can use the same data through the web app, a text editor, or the CLI.
8
+
9
+ ![FileGRC SOC 2 program overview](docs/filegrc-home.png)
10
+
11
+ ## Why it exists
12
+
13
+ SOC 2 work tends to scatter across documents, calendars, tickets, screenshots, and the auditor’s request list. That makes it hard to answer basic questions: What is due? Which policy requires it? What changed during the audit period? Is the evidence complete?
14
+
15
+ FileGRC keeps that work connected:
16
+
17
+ - A starter Security program links criteria references, policies, planned controls, owners, and schedules.
18
+ - The obligation board turns policy timing into upcoming, due, and overdue work.
19
+ - Event checklists cover hiring, departures, vendor changes, incidents, and other policy triggers.
20
+ - Audit Readiness says what management work, source-system exports, and evidence are still missing.
21
+ - The packet builder produces a scoped, indexed delivery with source files, attachments, history, and checksums.
22
+
23
+ The starter content is a proposal, not a claim of compliance. Review every policy and planned control against how your company actually operates before approving it.
24
+
25
+ ## Start a workspace
26
+
27
+ You need Node.js 20 or newer and Git.
28
+
29
+ ```sh
30
+ npx create-filegrc@latest company-grc
31
+ cd company-grc
32
+ npm run validate
33
+ npm run serve
34
+ ```
35
+
36
+ Setup asks for the company name, the initial policy owner, and a security contact email. It initializes Git when needed. The first local run then helps define the service boundary, an independent approver, and an optional audit goal.
37
+
38
+ Open the printed local URL. You can commit locally from Repository without configuring a remote. Add a remote when the team is ready to share the workspace, then the browser can pull with rebase and push reviewed commits.
39
+
40
+ ## How it works
41
+
42
+ 1. Add or edit compliance artifacts under `data/`.
43
+ 2. FileGRC validates relationships, renders the current program, and calculates policy work.
44
+ 3. Commit focused changes with a message that explains why the records changed.
45
+ 4. Git preserves the author, time, message, diff, and prior version.
46
+ 5. For an audit, define the Type 1 date or Type 2 period and generate an evidence packet from the selected revision.
47
+
48
+ Long-form policies, procedures, plans, minutes, training, assertions, and audit responses are Markdown companions beside their JSON records. Screenshots, signed acknowledgements, reports, and fixed exports are attachments linked through evidence records.
49
+
50
+ ## Run the program
51
+
52
+ Use Overview to follow the audit chain: scope, criteria, policies, controls, operation, and audit. The sidebar follows the same order.
53
+
54
+ Use Obligation Board for recurring work and event checklists. Each item shows its allowed completion window and overdue cutoff, based on the policy that created it. Link a dated completion record and evidence to close the occurrence.
55
+
56
+ Use the resource pages to maintain systems, people, vendors, risks, controls, tests, incidents, training, meetings, and evidence. The question-mark guide on each list explains what the record type is for, which policies call for it, and when to update it.
57
+
58
+ Agents use the same logic headlessly:
59
+
60
+ ```sh
61
+ npx filegrc guide risk-assessment --json
62
+ npx filegrc scaffold risk-assessment --title "2026 Annual Risk Assessment"
63
+ npx filegrc list risk --json
64
+ npx filegrc obligations --json
65
+ npx filegrc complete obligation-id completion-record.json
66
+ npx filegrc trigger person-started --occurred-on 2026-07-25 --subject person-id
67
+ npx filegrc complete-action action-item-id completion-record.json --completed-on 2026-07-25
68
+ npx filegrc complete-event obligation-event-id --completed-on 2026-07-25
69
+ npx filegrc search "access review"
70
+ ```
71
+
72
+ `guide` reports the policy context, timing, required fields, valid values, relationship candidates, and Markdown locations for every resource type. `scaffold` produces the same JSON and Markdown mutation shape used by the browser. Read `AGENTS.md` and `data/AGENTS.md` for the full headless workflow.
73
+
74
+ ## Prepare the audit
75
+
76
+ Record the audit firm, scope, and exact date or period. Audit Readiness then checks management-owned preparation, including the system description, assertion, policy and control state, source systems, evidence, and Type 2 populations.
77
+
78
+ ![FileGRC audit readiness](docs/filegrc-audit.png)
79
+
80
+ For a Type 2 audit, reconcile each complete period population to its authoritative system after the period closes. A zero-item population still needs its source export and query. FileGRC packages the selected records, Markdown, fixed attachments, historical versions, indexes, and SHA-256 checksums.
81
+
82
+ ```sh
83
+ npx filegrc prepare-audit audit-id
84
+ npx filegrc audit-readiness audit-id --json
85
+ npx filegrc evidence-packet --audit audit-id
86
+ ```
87
+
88
+ FileGRC checks management preparation and packet integrity. The independent CPA firm still selects samples, tests controls, evaluates exceptions, decides whether evidence is sufficient, and issues the SOC 2 report.
89
+
90
+ ## What belongs elsewhere
91
+
92
+ FileGRC does not replace workforce, identity, source-control, deployment, infrastructure, monitoring, endpoint, backup, vulnerability, training, signature, procurement, contract, or vendor-risk systems.
93
+
94
+ Catalog each authoritative system in FileGRC, record how to export from it, and attach or reference the fixed evidence when Audit Readiness asks for it. The generated external-delivery index identifies files that still need to be supplied through an auditor portal or another approved channel.
95
+
96
+ The starter uses the SOC 2 Security category and does not include licensed criteria text. Add Availability, Processing Integrity, Confidentiality, or Privacy only when they are in scope.
97
+
98
+ ## Repository safety
99
+
100
+ The editable server has no authentication and binds to loopback by default. Do not expose it to an untrusted network. Use `npm run build` for a read-only site.
101
+
102
+ Do not put secrets or personal data that may need erasure into Git. Read `AGENTS.md` before broad record changes or automation work.