Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions enterprise-api-rate-limit-guard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Enterprise API Rate-Limit Contract Guard

Self-contained Enterprise Tooling slice for SCIBASE issue #19.

This guard evaluates institutional API and webhook integrations before production activation. It focuses on contract-aligned API limits, endpoint/export scope, retry behavior, idempotency, webhook signing, private-project safeguards, and admin dashboard evidence. It does not make live API calls, provision credentials, deliver webhooks, or touch real customer data.

## What It Checks

- signed enterprise API contract evidence and escalation owners
- sustained, burst, daily export, and concurrent job limits against the contract
- endpoint and export target scopes against approved contract scopes
- Retry-After and accepted backoff behavior before production key activation
- idempotency-key coverage for mutating endpoints
- private-project access safeguards such as IP allowlists and admin review packets
- webhook signing algorithm, secret rotation, dead-letter queue, and retry storm risk
- sandbox load-test recency and admin dashboard/cost-center tagging

## Files

- `index.js` - deterministic evaluator and audit digest helpers
- `sample-data.js` - synthetic institutional integration scenarios
- `test.js` - dependency-free regression tests
- `demo.js` - generates reviewer JSON, Markdown, and SVG artifacts in `reports/`
- `make-demo-video.py` - optional short MP4 demo generator for bounty review
- `REQUIREMENT_MAP.md` - maps this narrow module to issue #19 Enterprise Tooling requirements

## Validation

```bash
npm run check
npm test
npm run demo
python make-demo-video.py
```

The demo emits:

- `reports/activation-packet.json`
- `reports/activation-report.md`
- `reports/summary.svg`
- `reports/demo.mp4`

The included sample output releases one integration, reviews one integration, and holds two integrations, demonstrating blocker and warning paths for over-provisioned rate limits, scope drift, missing Retry-After handling, missing idempotency keys, private-project safeguards, stale webhook secrets, and missing admin evidence.
14 changes: 14 additions & 0 deletions enterprise-api-rate-limit-guard/REQUIREMENT_MAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Requirement Map

Issue #19 describes Enterprise Tooling for institutional dashboards, APIs, webhooks, and export pipelines. This module implements a narrow pre-production guard that checks whether an institutional API or webhook integration is safe to activate under its signed contract and admin evidence.

| Issue area | Module coverage |
| --- | --- |
| Secure RESTful API integrations | Reconciles production API limits and endpoint scopes against the signed enterprise contract. |
| Webhook support | Checks signing algorithm, secret rotation, retry limits, and dead-letter readiness. |
| Institutional repositories and export pipelines | Validates approved export targets and daily export limits before archive delivery. |
| Admin dashboards | Requires owner/escalation contacts and dashboard tags for institutional visibility. |
| Usage stats and compute/storage awareness | Emits sustained/burst/daily/concurrency limit decisions for admin usage review. |
| Compliance tracking | Holds private-project integrations without IP allowlists or admin review packets. |
| Interoperability at scale | Blocks over-provisioned or unsafe integrations before production key activation. |
| Reviewer evidence | Generates deterministic JSON, Markdown, SVG, and MP4 artifacts from synthetic data only. |
101 changes: 101 additions & 0 deletions enterprise-api-rate-limit-guard/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const fs = require("node:fs");
const path = require("node:path");
const { evaluatePortfolio } = require("./index");
const { sampleIntegrations } = require("./sample-data");

const reportDir = path.join(__dirname, "reports");
fs.mkdirSync(reportDir, { recursive: true });

const portfolio = evaluatePortfolio(sampleIntegrations);
const jsonPath = path.join(reportDir, "activation-packet.json");
const markdownPath = path.join(reportDir, "activation-report.md");
const svgPath = path.join(reportDir, "summary.svg");

fs.writeFileSync(jsonPath, `${JSON.stringify(portfolio, null, 2)}\n`);
fs.writeFileSync(markdownPath, renderMarkdown(portfolio));
fs.writeFileSync(svgPath, renderSvg(portfolio));

console.log(`status=${portfolio.status}`);
console.log(`blockers=${portfolio.metrics.blockers}`);
console.log(`warnings=${portfolio.metrics.warnings}`);
console.log(`auditDigest=${portfolio.auditDigest}`);
console.log(`wrote ${path.relative(process.cwd(), jsonPath)}`);
console.log(`wrote ${path.relative(process.cwd(), markdownPath)}`);
console.log(`wrote ${path.relative(process.cwd(), svgPath)}`);

function renderMarkdown(portfolio) {
const rows = portfolio.integrations
.map(
(item) =>
`| ${item.id} | ${item.status} | ${item.blockers.length} | ${item.warnings.length} | ${item.contractedSustainedRpm} | ${item.provisionedSustainedRpm} |`,
)
.join("\n");
const actions = portfolio.integrations
.flatMap((item) => item.actions.map((action) => `- ${item.id}: ${action}`))
.join("\n");

return `# Enterprise API Rate-Limit Contract Guard Report

Status: ${portfolio.status}

Audit digest: \`${portfolio.auditDigest}\`

## Summary

- Integrations checked: ${portfolio.metrics.totalIntegrations}
- Ready to release: ${portfolio.metrics.release}
- Needs review: ${portfolio.metrics.review}
- Held: ${portfolio.metrics.hold}
- Blockers: ${portfolio.metrics.blockers}
- Warnings: ${portfolio.metrics.warnings}

## Activation Decisions

| Integration | Status | Blockers | Warnings | Contract RPM | Provisioned RPM |
| --- | --- | ---: | ---: | ---: | ---: |
${rows}

## Remediation Actions

${actions}
`;
}

function renderSvg(portfolio) {
const cards = portfolio.integrations
.map((item, index) => {
const y = 150 + index * 96;
const color = item.status === "release" ? "#0f766e" : item.status === "review" ? "#b45309" : "#b91c1c";
return `<g>
<rect x="60" y="${y}" width="1040" height="72" rx="8" fill="#ffffff" stroke="#d1d5db"/>
<text x="90" y="${y + 28}" font-size="22" font-weight="700" fill="#111827">${escapeXml(item.name)}</text>
<text x="90" y="${y + 54}" font-size="16" fill="#4b5563">${escapeXml(item.institution)} - ${escapeXml(item.integrationType)}</text>
<rect x="780" y="${y + 18}" width="140" height="36" rx="6" fill="${color}"/>
<text x="850" y="${y + 42}" text-anchor="middle" font-size="16" font-weight="700" fill="#ffffff">${item.status.toUpperCase()}</text>
<text x="950" y="${y + 32}" font-size="15" fill="#374151">B ${item.blockers.length} / W ${item.warnings.length}</text>
</g>`;
})
.join("\n");

return `<svg xmlns="http://www.w3.org/2000/svg" width="1160" height="600" viewBox="0 0 1160 600">
<rect width="1160" height="600" fill="#f8fafc"/>
<text x="60" y="62" font-size="34" font-weight="800" fill="#111827">Enterprise API Rate-Limit Contract Guard</text>
<text x="60" y="98" font-size="18" fill="#4b5563">Pre-production activation checks for API limits, scopes, retries, webhooks, and admin evidence.</text>
<g>
<rect x="60" y="118" width="240" height="44" rx="8" fill="#111827"/>
<text x="180" y="147" text-anchor="middle" font-size="17" font-weight="700" fill="#ffffff">${portfolio.status}</text>
<text x="330" y="147" font-size="17" fill="#374151">${portfolio.metrics.blockers} blockers, ${portfolio.metrics.warnings} warnings across ${portfolio.metrics.totalIntegrations} integrations</text>
</g>
${cards}
<text x="60" y="560" font-size="13" fill="#6b7280">Synthetic demo data only. Audit digest ${portfolio.auditDigest.slice(0, 24)}...</text>
</svg>
`;
}

function escapeXml(value) {
return String(value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
Loading