Quorum-based verification for CI pipelines. Run checks independently across multiple nodes and fail only when a quorum agrees.
Run the same check independently. Fail only when most executions agree.
- name: Verify API Health
uses: quorumci/action@v1
id: health
with:
type: http
url: https://api.example.com/health
executions: '3'
quorum: '2'
- name: Check Result
run: echo "Verdict: ${{ steps.health.outputs.verdict }}"- name: Test Webhook
uses: quorumci/action@v1
with:
type: webhook
endpoint: https://hooks.example.com/deploy
payload: '{"environment": "staging"}'
headers: '{"Authorization": "Bearer ${{ secrets.WEBHOOK_TOKEN }}"}'- name: Run Verification Script
uses: quorumci/action@v1
with:
type: script
script-file: ./scripts/verify.js
runtime: node
script-env: '{"API_KEY": "${{ secrets.API_KEY }}"}'- name: Verify DNS Records
uses: quorumci/action@v1
with:
type: dns
hostname: example.com
record-type: A
expected-values: '93.184.216.34'- name: Check Certificate Expiry
uses: quorumci/action@v1
with:
type: tls
hostname: example.com
port: '443'
min-days-valid: '30'
expected-issuer: "Let's Encrypt"| Input | Description |
|---|---|
type |
Job type: http, webhook, script, dns, or tls |
| Input | Default | Description |
|---|---|---|
url |
- | Target URL (required for http type) |
method |
GET |
HTTP method (GET, POST, PUT, DELETE, HEAD, PATCH) |
body |
- | Request body |
| Input | Default | Description |
|---|---|---|
endpoint |
- | Webhook endpoint URL (required for webhook type) |
payload |
- | JSON payload (required for webhook type) |
| Input | Default | Description |
|---|---|---|
script |
- | Inline script code |
script-file |
- | Path to script file |
runtime |
node |
Script runtime (node or bash) |
script-args |
- | Script arguments (comma-separated) |
script-env |
- | Environment variables (JSON format) |
Either script or script-file is required for script jobs.
| Input | Default | Description |
|---|---|---|
hostname |
- | Hostname to resolve (required for dns type) |
record-type |
A |
DNS record type (A, AAAA, CNAME, MX, TXT, NS, SOA, PTR) |
expected-values |
- | Expected DNS values (comma-separated, partial match) |
nameserver |
- | Custom DNS server (e.g., 8.8.8.8) |
| Input | Default | Description |
|---|---|---|
hostname |
- | Hostname to check (required for tls type) |
port |
443 |
TLS port |
min-days-valid |
- | Minimum days until certificate expiry |
expected-issuer |
- | Expected certificate issuer (partial match) |
| Input | Default | Description |
|---|---|---|
headers |
- | Request headers (JSON format) |
expected-status |
200 |
Expected HTTP status code(s), comma-separated |
executions |
3 |
Number of executions (N) |
quorum |
2 |
Required agreement (M) |
timeout-ms |
30000 |
Timeout in milliseconds |
| Output | Description |
|---|---|
verdict |
Quorum verdict: pass, fail, or inconclusive |
quorum-met |
Whether quorum was achieved (true or false) |
agreement-count |
Number of agreeing executions |
result-json |
Full QuorumResult as JSON |
QuorumCI executes your check independently across multiple nodes. The check only fails when M of N executions agree on failure, reducing false positives from:
- Transient network issues
- Regional outages
- Flaky dependencies
- Single-point-of-failure runners
┌─────────────────────────────────────────┐
│ QuorumCI Action │
├─────────────────────────────────────────┤
│ Execution 1 ──┐ │
│ Execution 2 ──┼──► Quorum ──► Verdict │
│ Execution 3 ──┘ (M of N) │
└─────────────────────────────────────────┘
The action writes a detailed summary to the GitHub Step Summary, including:
- Overall verdict with visual indicator
- Agreement statistics
- Execution breakdown with individual results
- Timing information
name: API Health Check
on:
schedule:
- cron: '*/15 * * * *' # Every 15 minutes
jobs:
verify:
runs-on: ubuntu-latest
strategy:
matrix:
endpoint:
- https://api.example.com/health
- https://api.example.com/status
- https://api.example.com/ready
steps:
- name: Check ${{ matrix.endpoint }}
uses: quorumci/action@v1
with:
type: http
url: ${{ matrix.endpoint }}
executions: '3'
quorum: '2'- name: Verify Create Endpoint
uses: quorumci/action@v1
with:
type: http
url: https://api.example.com/test
method: POST
headers: '{"Content-Type": "application/json"}'
body: '{"test": true}'
expected-status: '201'- name: Verify Resource
uses: quorumci/action@v1
with:
type: http
url: https://api.example.com/resource
expected-status: '200,201,204'- name: Critical Check
uses: quorumci/action@v1
with:
type: http
url: https://api.example.com/critical
executions: '5'
quorum: '4'
timeout-ms: '60000'- name: Verify Production Ready
uses: quorumci/action@v1
id: verify
with:
type: http
url: https://staging.example.com/health
- name: Deploy to Production
if: steps.verify.outputs.verdict == 'pass'
run: ./deploy.sh production- name: Run Verification Script
uses: quorumci/action@v1
with:
type: script
script: |
const response = await fetch(process.env.API_URL);
if (!response.ok) process.exit(1);
console.log('OK');
runtime: node
script-env: '{"API_URL": "https://api.example.com/health"}'- name: Run Bash Check
uses: quorumci/action@v1
with:
type: script
script: |
curl -sf https://api.example.com/health > /dev/null
exit $?
runtime: bash- name: Verify API
uses: quorumci/action@v1
id: check
with:
type: http
url: https://api.example.com/health
- name: Process Results
run: |
echo "Verdict: ${{ steps.check.outputs.verdict }}"
echo "Agreement: ${{ steps.check.outputs.agreement-count }}"
# Parse full result
RESULT='${{ steps.check.outputs.result-json }}'
echo "Duration: $(echo $RESULT | jq -r '.durationMs')ms"Ensure you've set type: http and provided the url input:
with:
type: http
url: https://api.example.com/health # Don't forget this!- Check if your endpoint is returning the expected status code
- Increase
timeout-msif requests are timing out - Use
expected-statusto accept multiple valid status codes - Check the Step Summary for individual execution details
- Ensure you provide either
script(inline) orscript-file(path) - Check that
runtimeis eithernodeorbash - For file scripts, ensure the file exists in your repository
Increase the timeout for slow endpoints:
with:
timeout-ms: '60000' # 60 secondsMIT