Help us shape the future of Go! Take the 2026 Go Developer Survey

go-grc

module
v0.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: BSD-2-Clause

README

go-grc

Governance, Risk & Compliance (GRC) CLI for managing security compliance as code. Designed for the SirosID wallet platform but applicable to any project that tracks controls, audit findings, and framework mappings in YAML.

grc automates the full compliance lifecycle:

  1. Sync — create and update GitHub issues for each audit finding; discover linked PRs and collect evidence.
  2. Derive — compute control and mapping statuses bottom-up from findings and evidence (verified → in progress → to-do).
  3. Render — generate a complete Docusaurus compliance site with per-control pages, per-requirement pages for each framework, dashboards, finding summaries, risk register, and year-cycle calendar.
  4. Serve — run an HTTP server for the rendered site with optional GitHub webhook listener for automatic re-rendering on push.
  5. Export — produce an auditor-ready JSON evidence package.
  6. Status — print a read-only overview of current compliance posture.

Data model

grc works on a compliance repository with the following layout:

catalog/
  technical/        # Platform security controls (YAML)
  organizational/   # Operator controls (YAML)
  frameworks/       # Normative requirement text per framework
mappings/           # Framework → control cross-references (YAML)
  eudi-secreq.yaml  #   EUDI Wallet Security Requirements
  iso27001-annexa.yaml  #   ISO/IEC 27001:2022 Annex A
  gdpr.yaml         #   GDPR checklist
audits/             # Structured audit findings with evidence (YAML)
risk-register/      # Accepted risks with compensating controls (YAML)

Each control has an ID (e.g. SID-AUTH-01), a status (verified, in_progress, to_do), and references to source code, PRs, deployed endpoints, or external reports that serve as evidence.

Framework mappings

Mapping files cross-reference external requirements to internal controls. Supported frameworks:

Framework Standard Requirements
EUDI ENISA Wallet Security Requirements v0.5 85
FitCEM Nordic EUDIW Certification System – Wallet Instance FitCEM PP —
ISO 27001 ISO/IEC 27001:2022 Annex A 93
GDPR Regulation (EU) 2016/679 19
OWASP ASVS OWASP ASVS 4.0.3 Level 3 —
STRIDE Threat model mapping —
Risk register

Risks (accepted, transferred, monitoring, or draft proposals) are tracked in YAML files under risk-register/. Each risk links to a finding, documents compensating controls, residual severity, and review intervals. The rendered site shows an overview with severity badges and per-risk detail pages.

Every risk must have an owner: the person or role accountable for it, who approves the treatment plan and accepts the residual risk (ISO/IEC 27001:2022 6.1.2 c) 2), 6.1.3 e)). This is distinct from decision.reviewer (who assesses the analysis) and from the register-level owner (the team that owns the register file). grc validate and grc risk validate fail on a risk without one; grc risk list --owner NAME filters by it.

Projects can name their risk assessment methodology document with risk_register.methodology in .grc.yaml. It is treated as authoritative for the register: it is served to MCP clients as grc://risk/methodology, the MCP server instructions and risk_review prompt tell agents to check the register against it, and agents are told never to change the register's schema or assessment rules without the matching change to the document. grc validate fails if the configured file is missing. An accepted risk must record decision.owner_accepted_date (YYYY-MM-DD): when its owner accepted the residual risk and approved the treatment plan, distinct from the decision date and from decision.reviewer (the approving CISO). When a risk records consequence, likelihood and residual_likelihood, validation also checks that severity and residual_severity equal the level derived from them (consequence × likelihood: e.g. medium × possible = medium, medium × unlikely = low), so the recorded levels cannot drift from the assessment.

Every risk that is not a draft must also record a treatment_action: what is done to treat it, who does it, and its status. A draft may omit it but is validated if present.

treatment_action:
  action: "what is being done"            # required
  responsible: "person or role doing it"  # required
  status: open | in_progress | done       # required
  due_date: "YYYY-MM-DD"                  # optional target date
  completed_date: "YYYY-MM-DD"            # required when status is done; must not be set otherwise

It is rendered on the risk page (with the status in the index), included in grc risk list --format json and the grc://risk/register MCP resource, and grc risk summary counts risks by treatment status.

Year-cycle calendar

Recurring GRC activities (reviews, audits, penetration tests) can be loaded from an iCal (.ics) feed and projected onto a single canonical year. The rendered page shows a circular SVG year diagram and a month-by-month schedule.

Controls can reference calendar activities by a stable lookup key derived from the event title (e.g. DPIA Review → dpia-review) rather than calendar UIDs that may change over time.

Audits and internal assessments

Findings come in two kinds, kept in separate directories:

  • Real audits (audits.dir, default audits/): certification and surveillance audit cycles. One directory per cycle, e.g. audits/iso27001-2026-07/, holding audit.yaml (findings), soa.yaml (Statement of Applicability) and references/ (supporting material, never rendered). Flat audits/*.yaml findings files are still read and count as real audits.
  • Internal assessments (assessments.dir, default assessments/): self-assessments. They feed derivation, sync and the private site like any other findings, but are never rendered on the public profile.

A Statement of Applicability (soa: header plus requirements: rows of annex_a, title, applicable (true/false/partial), status (implemented/partial/gap), justification, evidence) is rendered as a table under /audits/<cycle>. A statement has a review state in soa.status: draft, in_review or approved (with approved_date). Only an approved statement is published; every other state, including a missing or unrecognized one, is internal. The private site shows all statements, marking unapproved ones with a banner; the public site shows only approved ones, drops the Audits navbar entry when there are none, and removes pages published by an earlier render when a statement goes back to draft. The evidence column, which points into the repository, is private only. grc validate checks every statement.

Status derivation

The derive command computes statuses bottom-up:

  • Finding → Control: a control with open findings is downgraded; a control whose findings are all resolved is upgraded to verified. If every one of those findings also carries evidence the control is additionally marked evidenced (a flag, not a separate status).
  • Control → Mapping: a mapping requirement whose controls are all verified is marked full; for result-style frameworks it is compliant only when they are also evidenced. Partial coverage yields partial.

Terminology: "verified" is the only control status for "implemented and confirmed". The old validated status is a deprecated alias, read as verified; "evidence" is tracked separately.

Installation

go install github.com/sirosfoundation/go-grc/cmd/grc@latest

Or build from source:

git clone https://github.com/sirosfoundation/go-grc.git
cd go-grc
make build          # → bin/grc
make install        # → $GOBIN/grc
Container image
docker pull ghcr.io/sirosfoundation/go-grc:latest

# Or build locally
make docker

Usage

All commands take a --root (-r) flag pointing at the compliance repository:

# Show current compliance posture
grc -r /path/to/compliance status

# Sync findings ↔ GitHub issues
grc -r /path/to/compliance sync

# Derive statuses (preview first, then apply)
grc -r /path/to/compliance derive --dry-run
grc -r /path/to/compliance derive

# Generate the Docusaurus compliance site (public or private)
grc -r /path/to/compliance render --profile public
grc -r /path/to/compliance render --profile private

# Serve the rendered site over HTTP
grc -r /path/to/compliance serve --profile private --addr :8080

# Serve with GitHub webhook for automatic re-rendering
grc -r /path/to/compliance serve --webhook --webhook-secret "$GRC_WEBHOOK_SECRET"

# Export evidence package for auditors
grc -r /path/to/compliance export -o /tmp/audit-export
Docker usage

The Docker image includes Node.js and pnpm so that grc serve can run Docusaurus builds inside the container. On startup the server renders markdown, runs pnpm install + docusaurus build, and then serves the styled HTML site.

# Serve the compliance site (renders + builds on startup)
docker run -v /path/to/compliance:/data \
  -p 8080:8080 \
  ghcr.io/sirosfoundation/go-grc \
  serve -r /data --profile private

# With GitHub webhook and daily scheduled rebuilds
docker run -v /path/to/compliance:/data \
  -e GRC_WEBHOOK_SECRET=your-secret \
  -p 8080:8080 \
  ghcr.io/sirosfoundation/go-grc \
  serve -r /data --webhook --rebuild-interval 24h

# Webhook rebuilds are debounced: a burst of pushes yields one rebuild, started
# after --webhook-debounce (default 30s) of quiet. Builds never overlap; a push
# during a build queues exactly one follow-up. 0 rebuilds immediately.

# One-shot render (markdown only, no Docusaurus build)
docker run --rm -v /path/to/compliance:/data \
  ghcr.io/sirosfoundation/go-grc \
  render -r /data --profile public
Docker Compose

Serve both public and private dashboards side by side. A named volume for node_modules caches npm dependencies across container restarts so only the Docusaurus build (~20 s) runs instead of a full pnpm install (~2 min):

# docker-compose.yml
services:
  public:
    image: ghcr.io/sirosfoundation/go-grc:latest
    volumes:
      - .:/data
      - public-site-cache:/data/site/node_modules
    command: ["serve", "-r", "/data", "--profile", "public", "--addr", ":8080"]
    ports:
      - "8080:8080"
    restart: unless-stopped

  private:
    image: ghcr.io/sirosfoundation/go-grc:latest
    volumes:
      - .:/data
      - private-site-cache:/data/site/node_modules
    command: ["serve", "-r", "/data", "--profile", "private", "--addr", ":8080"]
    ports:
      - "8081:8080"
    restart: unless-stopped

volumes:
  public-site-cache:
  private-site-cache:
docker compose up -d
# Public dashboard:  http://localhost:8080
# Private dashboard: http://localhost:8081

To enable automatic re-rendering on GitHub push, add a webhook service:

  webhook:
    image: ghcr.io/sirosfoundation/go-grc:latest
    volumes:
      - .:/data
      - webhook-site-cache:/data/site/node_modules
    command: ["serve", "-r", "/data", "--profile", "private", "--addr", ":8080", "--webhook", "--rebuild-interval", "24h"]
    ports:
      - "8082:8080"
    environment:
      - GRC_WEBHOOK_SECRET=${GRC_WEBHOOK_SECRET}
    restart: unless-stopped
CI pipeline

In a compliance repository with a Makefile wired to grc, the typical CI pipeline is:

make pipeline       # sync → derive → render
make build          # render + docusaurus build

Configuration

Project settings are stored in .grc.yaml at the repository root:

project:
  name: "My Compliance Dashboard"
  repo: "myorg/compliance"
  url: "https://compliance.example.com"

catalog:
  dir: catalog
  subdirs: [technical, organizational]

mappings:
  dir: mappings

audits:
  dir: audits            # real audit cycles: <cycle>/{audit,soa}.yaml
assessments:
  dir: assessments       # internal self-assessments, never public

site:
  dir: site/docs

frameworks:
  - id: iso27001
    name: "ISO 27001 Annex A"
    catalog_file: iso27001-annexa.yaml
    mapping_file: iso27001-annexa.yaml
    key_field: annex_a

risk_register:
  dir: risk-register
  files: [platform.yaml]

year_cycle:
  title: "Compliance Year Cycle"
  source: "https://calendar.google.com/calendar/ical/.../basic.ics"
  public: false

Architecture

cmd/grc/
  main.go           CLI entrypoint (cobra)
  sync/             GitHub ↔ findings synchronisation
  derive/           Bottom-up status derivation
  render/           Docusaurus page generation (controls, frameworks,
                    risk register, year-cycle, CSF overview, findings)
  serve/            HTTP server with GitHub webhook support
  export/           Evidence package export
  status/           Read-only status overview
  validate/         Config and data validation
  initialize/       Scaffold a new compliance repository
  oscal/            OSCAL SSP export
  risk/             Risk register CLI operations

pkg/
  catalog/          Control catalog + framework catalog loaders (YAML)
  audit/            Finding & evidence types, audit file loader/saver
  mapping/          Framework mapping types and loader
  config/           Project configuration (.grc.yaml) and path resolution
  github/           go-github wrapper for issue/PR operations
  risk/             Risk register types and loader
  yearcycle/        iCal parser + year-cycle projection

Development

make setup          # Install golangci-lint, goimports, gosec, staticcheck
make test           # Run tests with -race
make lint           # Run golangci-lint
make coverage       # Generate coverage report
make fmt            # gofmt + goimports
make vet            # go vet
make docker         # Build Docker image

OpenSSF Scorecard

License

BSD 2-Clause — see LICENSE.

Directories

Path Synopsis
cmd
grc command
grc/serve
Package serve implements the "grc serve" command — an HTTP server that serves the rendered compliance site and optionally listens for GitHub webhooks to trigger automatic re-renders.
Package serve implements the "grc serve" command — an HTTP server that serves the rendered compliance site and optionally listens for GitHub webhooks to trigger automatic re-renders.
internal
testutil
Package testutil holds helpers shared by the command tests.
Package testutil holds helpers shared by the command tests.
pkg
catalog
Package catalog provides types and loading for the control catalog.
Package catalog provides types and loading for the control catalog.
github
Package github provides a client for syncing compliance findings with GitHub issues.
Package github provides a client for syncing compliance findings with GitHub issues.
mapping
Package mapping provides generic types and I/O for framework-to-control mappings.
Package mapping provides generic types and I/O for framework-to-control mappings.
soa
Package soa loads and validates Statements of Applicability.
Package soa loads and validates Statements of Applicability.
yearcycle
Package yearcycle loads recurring events from an iCal feed and projects them onto a single canonical year for cyclic visualization.
Package yearcycle loads recurring events from an iCal feed and projects them onto a single canonical year for cyclic visualization.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL