Skip to main content

Bring Your Own Policy

HubHelper's compliance engine lets you define exactly what "compliant" means for your organisation. You write the rules; HubHelper enforces them.

What this means in practice

Your policy lives in a JSON file in a repository you control. Update the file and the next scan automatically picks up the new rules — no HubHelper version update, no code change, no redeployment.


How It Works

The compliance pipeline has three layers:

Policy Repository (.hubhelper/approved-emails.json)


GitHubFetcher.getApprovedEmailConfig() ← fetches & parses your policy


ComplianceChecker.checkAll() ← pure logic, no network calls


ComplianceResult ← structured violations report

The ComplianceAnalyzer orchestrates the fetcher and the checker, then returns a ComplianceResult you can consume in any reporter.


Setting Up Your Policy Repository

Step 1 — Create the config file

In any repository inside your organisation, create the directory and file:

your-policy-repo/
└── .hubhelper/
└── approved-emails.json

Step 2 — Write your policy

.hubhelper/approved-emails.json
{
"domains": [
"acme.com",
"subsidiary.acme.com"
],
"exactEmails": [
"contractor@external.dev",
"partner@vendor.io"
]
}

Step 3 — Commit and push

git add .hubhelper/approved-emails.json
git commit -m "chore: add HubHelper compliance policy"
git push

That's it. HubHelper will fetch this file via the GitHub API each time a compliance check runs.


Policy Schema Reference

The policy file must be valid JSON matching the ApprovedEmailConfig interface:

interface ApprovedEmailConfig {
domains: string[]; // Required — approved email domains
exactEmails?: string[]; // Optional — individual address exceptions
}

domains (required)

An array of approved email domains. All comparisons are case-insensitive.

{
"domains": ["acme.com", "partner.io"]
}

A member passes the email check if their GitHub profile email ends with @<domain> where <domain> appears in this list.

exactEmails (optional)

An array of individual email addresses that are always approved, regardless of domain. Useful for contractors, consultants, or long-term partners who use personal or external addresses.

{
"domains": ["acme.com"],
"exactEmails": ["trusted-contractor@external.dev"]
}

Compliance Rules

Rule: missing_full_name

Passes when the member's GitHub profile has a non-empty full name (trimmed).
Fails when name is null or contains only whitespace.

// ✓ Passes
{ "name": "Jane Smith", "email": "jane@acme.com" }

// ✗ Fails
{ "name": null, "email": "jane@acme.com" }
{ "name": " ", "email": "jane@acme.com" }

Rule: missing_approved_email

Passes when the email domain is in domains OR the full address is in exactEmails.
Fails when email is null, empty, malformed (no @), or the domain/address is not approved.

// ✓ Passes (domain match)
{ "email": "jane@acme.com" }

// ✓ Passes (exact match)
{ "email": "contractor@external.dev" }

// ✗ Fails
{ "email": "jane@personal.email" }
{ "email": null }

Running a Compliance Check

Use the compliance sub-command (pass the repo name that contains your policy):

npx @sdh100shaun/hubhelper analyze \
--org acme-corp \
--token $GITHUB_TOKEN \
--compliance-repo policy-repo

You can also specify a custom path to the config file:

npx @sdh100shaun/hubhelper analyze \
--org acme-corp \
--compliance-repo policy-repo \
--compliance-config security/hubhelper-policy.json

The default path is .hubhelper/approved-emails.json.


Interpreting Results

A compliance run returns a ComplianceResult:

{
"organization": "acme-corp",
"totalMembers": 42,
"compliantMembers": 39,
"nonCompliantMembers": [
{
"user": "jsmith99",
"violations": ["missing_approved_email"],
"details": {
"name": "John Smith",
"email": "john@personal.email"
}
},
{
"user": "devx42",
"violations": ["missing_full_name"],
"details": {
"name": null,
"email": "dev@acme.com"
}
}
],
"checkedAt": "2026-04-24T09:15:00.000Z"
}
FieldTypeDescription
organizationstringThe GitHub organisation name
totalMembersnumberAll org members checked
compliantMembersnumberMembers with zero violations
nonCompliantMembersComplianceViolation[]Members with at least one violation
checkedAtstringISO 8601 timestamp of the check

Each ComplianceViolation contains:

FieldTypeDescription
userstringGitHub login handle
violationsComplianceViolationType[]One or more rule failures
details.namestring | nullCurrent profile name
details.emailstring | nullCurrent profile email

Real-World Examples

Small Startup — Single Domain

{
"domains": ["startup.io"]
}

Every team member must use their @startup.io address. No exceptions needed yet.

Enterprise — Multiple Domains + Contractors

{
"domains": [
"bigcorp.com",
"bigcorp-eu.com",
"acquired-startup.com"
],
"exactEmails": [
"consultant@partner-firm.com",
"auditor@external-audit.org"
]
}

Covers all org email domains after an acquisition, plus two recurring external contacts.

Strict Security Team

{
"domains": ["secureteam.io"],
"exactEmails": []
}

No exceptions — every member must have an approved domain address. Empty exactEmails is valid (or omit the field entirely).


CI/CD Integration

Run compliance checks automatically on a schedule:

.github/workflows/compliance.yml
name: Compliance Check

on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
workflow_dispatch:

jobs:
compliance:
runs-on: ubuntu-latest
steps:
- name: Run HubHelper Compliance Check
run: |
npx @sdh100shaun/hubhelper analyze \
--org ${{ vars.GITHUB_ORG }} \
--token ${{ secrets.HUBHELPER_TOKEN }} \
--compliance-repo policy-repo \
--json compliance-report.json

- name: Upload Report
uses: actions/upload-artifact@v4
with:
name: compliance-report
path: compliance-report.json
retention-days: 90

Security Considerations

  • Keep your policy repo private if it contains sensitive domain information.
  • HubHelper needs only read access to the policy repo (contents: read).
  • The token used for compliance checks never writes back to any repository.
  • Policy JSON is fetched fresh on every run — no caching of stale rules.