Skip to content

Continuous Control Monitoring and GRC Architecture

Purpose. Describes Soon's home-grown GRC — how we automatically and continuously collect evidence and monitor that controls are operating, connecting live systems (AWS, GitHub, Google Workspace, WorkOS, Aikido, …) to the ISMS. It is the automation layer on top of the control catalog, policies, evidence store and dashboards Soon already has. Supports clause 9.1 (monitoring, measurement, analysis, evaluation) and controls A.8.16 (monitoring activities) and A.5.36 (compliance monitoring) — and will satisfy them once operating.

Status: DRAFT — collectors running daily since 2026-07-12 (SoonHQ/soon-grc, AWS via OIDC); PR-to-register publishing not yet enabled. This document is the blueprint and the honest go/no-go. The collectors themselves are built in Soon's app/infra repo, not here (see §2). Concrete account/repo/threshold choices are TODO(owner).

1. Can we build our own GRC? Yes — and we're ~half-way

A GRC platform is five things: (1) a control catalog mapped to frameworks, (2) policy management, (3) evidence collection, (4) continuous control monitoring (drift detection), and (5) dashboards/reporting. Soon already has 1, 2 and 5 in this repo:

GRC capability Soon already has Status
Control catalog + framework mapping Statement of Applicability, ROADMAP.md ✅
Policy management this repo (Markdown + tools/isms.py, PR-reviewed, human-approved) ✅
Evidence store + index Evidence Register + hardened S3 store ✅ designed
Dashboards STATUS.md (auto-generated) ✅
Automated evidence collection — ⬜ this is what we add
Continuous drift detection prompts/evidence-scan.md ("enforce with AI") 🟡 designed, manual

So "build our own GRC" = build the automated collection + continuous-monitoring layer and wire it to what exists. We are not starting from zero.

2. The one thing that stops us doing it here — and where it goes instead

This policy repo is documentation only — never application code or secrets (AGENTS.md). A GRC collector authenticates to AWS/GitHub/live environments; it is application code and it holds credentials. So the collectors cannot live in this repo. That is the hard boundary — not a blocker on building the GRC, just a rule about where each part lives:

  CONTROL PLANE — the "brain"  (THIS repo, no secrets)     DATA PLANE — the "hands"  (app/infra repo)
  ───────────────────────────────────────────────         ──────────────────────────────────────────
  • Control catalog / SoA / policies                        • Collectors: read-only API calls to AWS,
  • Automated Control Check Catalog (ISMS-FORM-09-6) ─spec─▶   GitHub, Google Workspace, WorkOS, Aikido…
  • Evidence Register (ISMS-FORM-07-2)   ◀──PR updates────   • AI drift-scan (prompts/evidence-scan.md)
  • Dashboards (STATUS.md)               ◀──results─────     • Runs on a schedule (GitHub Actions / Lambda)
  • Human review + approval (PRs)                            • Writes artefacts ──▶ S3 evidence store (WORM)

The bridge between the planes is exactly what we've already built: the S3 evidence store (artefacts land there), a PR back to this repo updating the Evidence Register (the same workflow every ISMS change uses), and prompts/evidence-scan.md (the AI drift check). The collector layer lives in the dedicated soon-grc repo — separate from this policy repo so no code or credentials ever enter here. soon-grc is scaffolded (read-only AWS/GitHub collectors, a check catalog mirroring ISMS-FORM-09-6, and a readiness dashboard); TODO(owner): connect it to the live environments and schedule it to make CCM operate.

3. Three collection modes (not everything auto-collects — be honest)

Mode What Good for Rough share of controls
API collector Read-only API/CLI call → JSON evidence + pass/fail vs a predicate Technical config state (backups, encryption, MFA, branch protection, public access) ~40–50%
AI drift-scan Scheduled Claude Code run of evidence-scan.md in the app/infra repo — reality-vs-policy "Does the code/IaC still match the policy?" overlaps the above
Attestation A human records that a process happened (training, screening, restore test, management review) People/process controls that have no API ~50–60%

A realistic home-grown GRC automates the technical controls and tracks the rest as attestations — it does not magically collect a signed NDA or a management-review decision. Each check in the catalog is tagged with its mode.

4. The loop (how a check runs, end to end)

  schedule (cron) ─▶ collector authenticates read-only ─▶ runs check from the Catalog
        │                                                          │
        │                                              pass/fail + evidence artefact
        │                                                          │
        │                          artefact ──▶ S3 evidence store (Object Lock / WORM)
        │                                                          │
        └──────────────▶ opens a PR to THIS repo: update Evidence Register row
                          (status, last-collected) + append CCM results
                                                   │
                                    merge (human) ─▶ STATUS.md dashboard refreshes
                                                   │
                                    FAIL / drift ─▶ alert to Slack #security  (a human fixes)

This mirrors Soon's existing "enforce with AI = detect drift, then a human fixes it" principle: the GRC detects and evidences; it does not auto-remediate production.

5. Security model for the collectors (do this right — Soon has had secret-hygiene gaps)

A service that can read everything is a high-value target. Non-negotiables:

  1. Read-only against monitored systems — least-privilege per integration: AWS SecurityAudit + ViewOnlyAccess; a GitHub App (short-lived installation token — a fine-grained PAT only as a rotated fallback held in the secrets manager); Google Workspace read-only Admin SDK scopes. The collector holds no write/admin on any monitored system. Its only write is proposing a PR to this policy repo (item 6), which cannot self-merge.
  2. No long-lived keys where avoidable. Prefer GitHub Actions OIDC → AWS role assumption (short-lived credentials). Any unavoidable tokens live in a secrets manager, never in code or .env. (Avoids reintroducing the Q1 plaintext-secret problem — see OPEN-QUESTIONS.md.)

One named exception, decided 2026-08-24: isms-site-uploader. The evidence upload box on the ISMS site runs as a Cloudflare Pages Function, which cannot assume an AWS role — Cloudflare is not an AWS OIDC principal — so it holds a long-lived access key in Cloudflare Pages encrypted environment variables.

The identity is scoped to one bucket and one key: s3:PutObject, s3:GetObject, s3:ListBucket, and the KMS actions needed to encrypt and decrypt with that key.

Amended 2026-08-25: read access was added deliberately. It was originally put-only, which was a stronger position but produced a system nobody could check — the team could file evidence and had no way to see what was filed, so a person could upload ten screenshots and not know whether any of them stuck. Evidence that cannot be reviewed is not evidence, so the endpoint now reads back what it wrote and renders it beside the control. It still cannot delete, and Object Lock forbids overwriting, so the worst outcome from a leak is an attacker adding objects or reading internal-confidential material — not destroying the record.

The alternative considered was uploading to Cloudflare R2 instead, which needs no credential at all. It was rejected: R2 would move only the evidence bucket, leaving the organisation CloudTrail and the isolated backup vault in AWS — two stores instead of one — and it would put the evidence in the same vendor account that serves the site, so a single Cloudflare compromise would take both the site and the record of what happened to it. Keeping evidence in AWS means the store and its front door fail independently.

Review: rotate the key annually, or immediately on any suspicion. Its use is visible in CloudTrail data events. 3. Put-only to S3. The collector can PutObject to the evidence store but cannot delete (Object Lock/WORM anyway). Its actions are logged via CloudTrail. 4. No self-marking. The collector proposes register updates via PR; a human merges, and Andrea's independent internal audit samples the evidence. This stops the GRC "marking its own homework." 5. Tight blast radius — collectors run in a controlled account/repo with restricted membership; rotate credentials; alert on collector auth failures. 6. The one write path. Updating the Evidence Register needs a GitHub identity that can open a PR against SoonHQ/isms (contents:write + pull_requests:write) — ideally a dedicated bot fork that holds zero write on the canonical repo. This is the highest-trust credential in the loop: scope it to the isms repo only, keep it in the secrets manager, rotate it. Branch protection (CHK-SDLC-01) enforces a human merge — the collector can never self-approve.

6. What would stop us — the honest assessment

# Consideration Blocker? Mitigation
1 Collectors + secrets can't live in the policy repo No Put them in the app/infra repo (§2)
2 Broad read-access is a security risk No Least-privilege, short-lived creds, logging (§5)
3 Maintenance burden — we own every integration as vendor APIs change No (real cost) Start narrow; automate only stable, high-value APIs; this is the main reason to consider buying
4 Breadth — commercial tools ship 100s of prebuilt checks + auditor workflows No Start with high-sample technical controls; grow the catalog
5 Auditor trust in home-grown evidence No Reliable, complete, tamper-evident (WORM), method documented — auditors accept this
6 Independence ("marking own homework") No PR review + independent audit (§5.4)

Verdict: nothing blocks it. The only non-negotiable is the repo boundary. The genuine trade-off is maintenance vs. buying (Vanta/Drata/Secureframe): build if you value control/cost and can carry upkeep on a 4-person team; buy for breadth/speed. A pragmatic path is hybrid — build API collectors for the core AWS/GitHub controls (high value, stable APIs), keep attestations for people/process, and re-evaluate a commercial tool if breadth becomes painful. TODO(owner): confirm build-vs-buy-vs-hybrid.

7. Phased build plan

Phase Build Where Output
0 Stand up the S3 evidence store + finalise the Check Catalog this repo + infra Store live; checks defined
1 (MVP) ~10 API collectors for the top technical controls (backup, encryption, S3 public-access, MFA, branch protection, access lists, vuln scans) on GitHub Actions + OIDC app/infra repo Nightly evidence → S3 + PR to register
2 Scheduled AI drift-scan (evidence-scan.md) + Slack #security drift alerts app/infra repo Reality-vs-policy alerts
3 Attestation tracking for people/process controls; coverage view (extend tools/isms.py or a small read-only dashboard) this repo Full coverage picture
4 Broaden the catalog; add the SOC 2 TSC cross-walk (for the GetAgency Type 1) both Multi-framework GRC

8. Roles

  • Olaf Jacobson (ISM) — owns the GRC design, the catalog and the control plane; reviews collector PRs.
  • Platform/infra (Thomas) — build and run the collectors in the app/infra repo; own the read-only credentials and the schedule.
  • Independent auditor (Andrea) — samples the automatically-collected evidence; confirms the collection method is trustworthy.

Change log

Version Date Author Comments
0.1 2026-07-12 ISMS First draft — home-grown GRC architecture: two-plane (control plane here / collectors in app-infra repo), three collection modes, the collect→store→PR→dashboard→alert loop, collector security model, honest "what would stop us", phased build plan.