diff --git a/README.md b/README.md
index d338cf6..3665781 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,6 @@
# deepevents.ai
deepevents.ai main codebase
+
+## Modules
+
+- [`enterprise-tooling`](./enterprise-tooling) - runnable prototype for institutional dashboards, enterprise integrations, webhooks, compliance analytics, and export pipelines.
diff --git a/enterprise-tooling/README.md b/enterprise-tooling/README.md
new file mode 100644
index 0000000..c0d7867
--- /dev/null
+++ b/enterprise-tooling/README.md
@@ -0,0 +1,57 @@
+# Enterprise Tooling
+
+This module is a self-contained implementation for SCIBASE.AI issue #19. It models enterprise admin dashboards, institutional APIs/webhooks, and export pipelines for research organizations.
+
+## What It Covers
+
+- Organization-wide admin dashboard with project, storage, compute, submission, review, and reproducibility metrics.
+- Contributor analytics with activity and collaboration signals.
+- Compliance tracking for funder mandates, open-access status, reproducibility scores, and internal tags.
+- Secure REST API catalog for institutional repositories, LMS, ELN, inventory, HRIS, and ORCID-style integrations.
+- Webhook payloads for project publication and review events.
+- Export pipelines for Zenodo, PubMed, arXiv, bioRxiv, journal, and funder-style handoffs.
+- Formatting plugin metadata that preserves DOI, ORCID, citations, and version history.
+
+## Run Locally
+
+```bash
+cd enterprise-tooling
+npm test
+npm start
+```
+
+Then open `http://localhost:4132`.
+
+## API Surface
+
+- `GET /api/dashboard`
+- `GET /api/enterprise/analytics`
+- `GET /api/enterprise/catalog`
+- `GET /api/enterprise/webhook-preview`
+- `GET /api/enterprise/export-preview?target=Zenodo&format=JATS`
+
+## Requirement Mapping
+
+- Admin dashboards: implemented by `buildAdminDashboard`.
+- Contributor analytics, usage stats, productivity metrics, compliance tracking, and tags: returned in the dashboard payload.
+- API and webhooks: implemented by `buildRestApiCatalog` and `createWebhookEvent`.
+- Institutional integrations: represented by DSpace, Canvas, Benchling, and ORCID sync metadata.
+- Export pipelines: implemented by `buildExportPipeline`.
+- Formatting plugins and preserved metadata: represented by pipeline plugin lists and preserved DOI/ORCID/citation/version fields.
+
+## Verification
+
+```bash
+npm test
+node src/server.js
+```
+
+Optional smoke checks:
+
+```bash
+curl -s http://localhost:4132/api/dashboard
+curl -s http://localhost:4132/api/enterprise/webhook-preview
+curl -s "http://localhost:4132/api/enterprise/export-preview?target=Zenodo&format=JATS"
+```
+
+Demo artifacts are committed under `docs/demo/`, including `dashboard.png` and `enterprise-tooling-demo.mp4`.
diff --git a/enterprise-tooling/docs/demo-script.md b/enterprise-tooling/docs/demo-script.md
new file mode 100644
index 0000000..0a22d99
--- /dev/null
+++ b/enterprise-tooling/docs/demo-script.md
@@ -0,0 +1,6 @@
+# Demo Script
+
+1. Run `npm test` to verify dashboard metrics, API catalog, webhooks, and export pipelines.
+2. Run `npm start` and open `http://localhost:4132`.
+3. Confirm the dashboard shows enterprise metrics, integrations, compliance rows, and export metadata.
+4. Smoke-test `/api/enterprise/webhook-preview` and `/api/enterprise/export-preview?target=Zenodo&format=JATS`.
diff --git a/enterprise-tooling/docs/demo/dashboard.png b/enterprise-tooling/docs/demo/dashboard.png
new file mode 100644
index 0000000..63dd67a
Binary files /dev/null and b/enterprise-tooling/docs/demo/dashboard.png differ
diff --git a/enterprise-tooling/docs/demo/enterprise-tooling-demo.mp4 b/enterprise-tooling/docs/demo/enterprise-tooling-demo.mp4
new file mode 100644
index 0000000..29ab051
Binary files /dev/null and b/enterprise-tooling/docs/demo/enterprise-tooling-demo.mp4 differ
diff --git a/enterprise-tooling/package.json b/enterprise-tooling/package.json
new file mode 100644
index 0000000..0d3ca7c
--- /dev/null
+++ b/enterprise-tooling/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "@scibase/enterprise-tooling",
+ "version": "0.1.0",
+ "private": true,
+ "description": "Self-contained enterprise tooling prototype for SCIBASE.AI issue #19.",
+ "type": "module",
+ "scripts": {
+ "start": "node src/server.js",
+ "test": "node --test test/*.test.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+}
diff --git a/enterprise-tooling/public/app.js b/enterprise-tooling/public/app.js
new file mode 100644
index 0000000..1120354
--- /dev/null
+++ b/enterprise-tooling/public/app.js
@@ -0,0 +1,28 @@
+const payload = await fetch("/api/dashboard").then((response) => response.json());
+
+document.querySelector("#orgName").textContent = payload.dashboard.organization;
+document.querySelector("#overview").innerHTML = [
+ row("Projects", payload.dashboard.overview.projects),
+ row("Storage", `${payload.dashboard.overview.storageGb} GB`),
+ row("Compute", `${payload.dashboard.overview.computeHours} hours`),
+ row("Average reproducibility", payload.dashboard.overview.averageReproducibility)
+].join("");
+
+document.querySelector("#api").innerHTML = payload.api.integrations
+ .map((integration) => `
${integration.name}${integration.type} · ${integration.enabled ? "enabled" : "disabled"}
`)
+ .join("");
+
+document.querySelector("#compliance").innerHTML = payload.dashboard.compliance
+ .map((item) => `${item.projectId}${item.mandate} · ${item.status} · score ${item.reproducibilityScore}
`)
+ .join("");
+
+document.querySelector("#export").innerHTML = [
+ row("Target", payload.exportPipeline.target),
+ row("Format", payload.exportPipeline.format),
+ row("Compliance", payload.exportPipeline.complianceStatus),
+ row("Preserves", payload.exportPipeline.preservedMetadata.join(", "))
+].join("");
+
+function row(label, value) {
+ return `${label}${String(value)}
`;
+}
diff --git a/enterprise-tooling/public/index.html b/enterprise-tooling/public/index.html
new file mode 100644
index 0000000..c1d416e
--- /dev/null
+++ b/enterprise-tooling/public/index.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+ SCIBASE Enterprise Tooling
+
+
+
+
+
+ SCIBASE.AI / issue #19
+ Enterprise Tooling
+
+
+
+ Admin Dashboard
+ Loading...
+
+
+
+ API & Webhooks
+ Institutional integrations
+
+
+
+ Compliance
+ Funder and open-access status
+
+
+
+ Export Pipeline
+ Publication handoff
+
+
+
+
+
+
+
diff --git a/enterprise-tooling/public/styles.css b/enterprise-tooling/public/styles.css
new file mode 100644
index 0000000..997428b
--- /dev/null
+++ b/enterprise-tooling/public/styles.css
@@ -0,0 +1,110 @@
+:root {
+ --ink: #141816;
+ --muted: #63706b;
+ --line: #d8dfda;
+ --paper: #f5f4ed;
+ --panel: #ffffff;
+ --green: #17664c;
+ --blue: #285f8e;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ background: var(--paper);
+ color: var(--ink);
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+.shell {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 32px;
+}
+
+header {
+ display: flex;
+ align-items: end;
+ justify-content: space-between;
+ gap: 24px;
+ margin-bottom: 24px;
+}
+
+p,
+h1,
+h2 {
+ margin: 0;
+}
+
+header p,
+.label {
+ color: var(--muted);
+ font-size: 12px;
+ font-weight: 900;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+h1 {
+ max-width: 760px;
+ font-size: clamp(38px, 6vw, 76px);
+ line-height: 0.95;
+}
+
+h2 {
+ margin-top: 8px;
+ font-size: 24px;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1.05fr 0.95fr;
+ gap: 16px;
+}
+
+.panel {
+ min-height: 260px;
+ border: 1px solid var(--line);
+ background: var(--panel);
+ padding: 24px;
+}
+
+.hero {
+ grid-row: span 2;
+}
+
+.row {
+ border-top: 1px solid var(--line);
+ padding-top: 12px;
+ margin-top: 14px;
+}
+
+.row span {
+ display: block;
+ color: var(--muted);
+}
+
+.row strong {
+ display: block;
+ color: var(--green);
+ overflow-wrap: anywhere;
+}
+
+.hero .row strong {
+ color: var(--blue);
+}
+
+@media (max-width: 820px) {
+ .shell {
+ padding: 18px;
+ }
+
+ header,
+ .grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/enterprise-tooling/src/enterprise-core.js b/enterprise-tooling/src/enterprise-core.js
new file mode 100644
index 0000000..6ef622e
--- /dev/null
+++ b/enterprise-tooling/src/enterprise-core.js
@@ -0,0 +1,138 @@
+export const organization = {
+ id: "org-university-research",
+ name: "University Research Office",
+ departments: ["Oncology", "Neuroscience", "Materials Science"],
+ projects: [
+ { id: "proj-organoid", title: "Organoid response atlas", department: "Oncology", visibility: "public", storageGb: 42, computeHours: 18, submissions: 3, aiReviews: 7, peerReviews: 5, reproducibilityScore: 94, openAccess: true, funderMandate: "NIH", tags: ["GRANT-TRACKED"] },
+ { id: "proj-neuro", title: "CRISPR neural clustering", department: "Neuroscience", visibility: "private", storageGb: 28, computeHours: 31, submissions: 1, aiReviews: 4, peerReviews: 2, reproducibilityScore: 87, openAccess: false, funderMandate: "Horizon EU", tags: ["DOCTORAL WORK"] },
+ { id: "proj-materials", title: "Polymer catalyst screening", department: "Materials Science", visibility: "institutional", storageGb: 64, computeHours: 44, submissions: 2, aiReviews: 6, peerReviews: 4, reproducibilityScore: 91, openAccess: true, funderMandate: "UKRI", tags: ["INDUSTRY"] }
+ ],
+ contributors: [
+ { id: "user-alice", name: "Alice Chen", lab: "Oncology", logins: 42, commits: 18, collaborations: ["Neuroscience"], orcid: "0000-0002-1825-0097" },
+ { id: "user-mateo", name: "Mateo Rivera", lab: "Open Reproducibility", logins: 33, commits: 21, collaborations: ["Oncology", "Materials Science"], orcid: "0000-0003-2201-4120" },
+ { id: "user-sam", name: "Sam Okafor", lab: "Materials Science", logins: 24, commits: 11, collaborations: ["Oncology"], orcid: "0000-0001-9911-4412" }
+ ],
+ integrations: [
+ { id: "dspace", name: "DSpace", type: "institutional-repository", endpoint: "https://repo.example/api", enabled: true },
+ { id: "canvas", name: "Canvas", type: "lms", endpoint: "https://canvas.example/api", enabled: true },
+ { id: "benchling", name: "Benchling", type: "eln", endpoint: "https://benchling.example/api", enabled: false },
+ { id: "orcid", name: "ORCID Sync", type: "personnel", endpoint: "https://orcid.org/api", enabled: true }
+ ]
+};
+
+export function buildAdminDashboard(org = organization) {
+ const totals = org.projects.reduce(
+ (acc, project) => {
+ acc.projects += 1;
+ acc.storageGb += project.storageGb;
+ acc.computeHours += project.computeHours;
+ acc.submissions += project.submissions;
+ acc.aiReviews += project.aiReviews;
+ acc.peerReviews += project.peerReviews;
+ acc.publicProjects += project.visibility === "public" ? 1 : 0;
+ acc.openAccess += project.openAccess ? 1 : 0;
+ acc.reproducibility += project.reproducibilityScore;
+ return acc;
+ },
+ { projects: 0, storageGb: 0, computeHours: 0, submissions: 0, aiReviews: 0, peerReviews: 0, publicProjects: 0, openAccess: 0, reproducibility: 0 }
+ );
+ return {
+ organization: org.name,
+ overview: { ...totals, averageReproducibility: Math.round(totals.reproducibility / totals.projects) },
+ departmentMetrics: org.departments.map((department) => summarizeDepartment(org, department)),
+ contributorAnalytics: org.contributors.map((contributor) => ({
+ ...contributor,
+ productivityScore: contributor.commits * 2 + contributor.logins + contributor.collaborations.length * 8
+ })),
+ compliance: org.projects.map((project) => ({
+ projectId: project.id,
+ mandate: project.funderMandate,
+ openAccess: project.openAccess,
+ reproducibilityScore: project.reproducibilityScore,
+ status: project.openAccess && project.reproducibilityScore >= 90 ? "compliant" : "attention"
+ })),
+ tags: [...new Set(org.projects.flatMap((project) => project.tags))]
+ };
+}
+
+export function buildRestApiCatalog(org = organization) {
+ return {
+ auth: "Bearer token scoped to institution",
+ routes: [
+ "GET /api/enterprise/projects",
+ "GET /api/enterprise/contributors",
+ "GET /api/enterprise/compliance",
+ "POST /api/enterprise/webhooks",
+ "POST /api/enterprise/exports"
+ ],
+ integrations: org.integrations,
+ scopes: ["read:projects", "read:analytics", "write:webhooks", "write:exports"]
+ };
+}
+
+export function createWebhookEvent(type, projectId, org = organization) {
+ const project = org.projects.find((item) => item.id === projectId);
+ if (!project) throw new Error(`Unknown project: ${projectId}`);
+ return {
+ id: `evt-${type}-${projectId}`,
+ type,
+ createdAt: "2026-05-09T00:00:00.000Z",
+ deliveryTargets: org.integrations.filter((integration) => integration.enabled).map((integration) => integration.endpoint),
+ payload: {
+ projectId,
+ title: project.title,
+ department: project.department,
+ visibility: project.visibility,
+ reproducibilityScore: project.reproducibilityScore,
+ tags: project.tags
+ }
+ };
+}
+
+export function buildExportPipeline({ projectId, target = "Zenodo", format = "JATS" }, org = organization) {
+ const project = org.projects.find((item) => item.id === projectId);
+ if (!project) throw new Error(`Unknown project: ${projectId}`);
+ const plugins = {
+ arXiv: ["LaTeX", "BibTeX", "PDF"],
+ bioRxiv: ["Docx", "JATS", "Figures"],
+ PubMed: ["JATS", "NIH metadata", "ORCID"],
+ Zenodo: ["DataCite DOI", "metadata.json", "version history"],
+ "NIH RePORTER": ["grant metadata", "public access compliance"]
+ };
+ return {
+ projectId,
+ target,
+ format,
+ steps: [
+ "validate project metadata",
+ `render manuscript as ${format}`,
+ "attach DOI, ORCID, citations, and version history",
+ `package repository for ${target}`,
+ "emit export.completed webhook"
+ ],
+ preservedMetadata: ["DOI", "ORCID", "citations", "version history", "funder mandate"],
+ plugins: plugins[target] || ["Docx", "metadata"],
+ complianceStatus: project.openAccess ? "ready" : "requires open-access review"
+ };
+}
+
+export function buildEnterprisePayload() {
+ return {
+ dashboard: buildAdminDashboard(),
+ api: buildRestApiCatalog(),
+ webhook: createWebhookEvent("project.published", "proj-organoid"),
+ exportPipeline: buildExportPipeline({ projectId: "proj-organoid", target: "Zenodo", format: "JATS" })
+ };
+}
+
+function summarizeDepartment(org, department) {
+ const projects = org.projects.filter((project) => project.department === department);
+ return {
+ department,
+ projectCount: projects.length,
+ computeHours: projects.reduce((sum, project) => sum + project.computeHours, 0),
+ storageGb: projects.reduce((sum, project) => sum + project.storageGb, 0),
+ aiReviews: projects.reduce((sum, project) => sum + project.aiReviews, 0),
+ peerReviews: projects.reduce((sum, project) => sum + project.peerReviews, 0)
+ };
+}
diff --git a/enterprise-tooling/src/server.js b/enterprise-tooling/src/server.js
new file mode 100644
index 0000000..0cb8b2b
--- /dev/null
+++ b/enterprise-tooling/src/server.js
@@ -0,0 +1,54 @@
+import http from "node:http";
+import { readFile } from "node:fs/promises";
+import { extname, join, resolve, sep } from "node:path";
+import { fileURLToPath } from "node:url";
+import { buildAdminDashboard, buildEnterprisePayload, buildExportPipeline, buildRestApiCatalog, createWebhookEvent } from "./enterprise-core.js";
+
+const root = join(fileURLToPath(new URL("..", import.meta.url)), "public");
+const port = Number(process.env.PORT || 4132);
+const contentTypes = { ".html": "text/html; charset=utf-8", ".css": "text/css; charset=utf-8", ".js": "text/javascript; charset=utf-8" };
+
+const server = http.createServer(async (request, response) => {
+ try {
+ const url = new URL(request.url, `http://${request.headers.host}`);
+ if (url.pathname === "/api/dashboard") return json(response, buildEnterprisePayload());
+ if (url.pathname === "/api/enterprise/analytics") return json(response, buildAdminDashboard());
+ if (url.pathname === "/api/enterprise/catalog") return json(response, buildRestApiCatalog());
+ if (url.pathname === "/api/enterprise/webhook-preview") return json(response, createWebhookEvent("project.published", "proj-organoid"));
+ if (url.pathname === "/api/enterprise/export-preview") return json(response, buildExportPipeline({ projectId: "proj-organoid", target: url.searchParams.get("target") || "Zenodo", format: url.searchParams.get("format") || "JATS" }));
+ return await serveStatic(url.pathname === "/" ? "/index.html" : url.pathname, response);
+ } catch (error) {
+ response.writeHead(500, { "content-type": "application/json; charset=utf-8" });
+ response.end(JSON.stringify({ error: error.message }));
+ }
+});
+
+server.listen(port, () => {
+ console.log(`Enterprise tooling demo running at http://localhost:${port}`);
+});
+
+function json(response, body) {
+ response.writeHead(200, { "content-type": "application/json; charset=utf-8" });
+ response.end(JSON.stringify(body, null, 2));
+}
+
+async function serveStatic(pathname, response) {
+ const filePath = resolve(root, pathname.replace(/^\/+/, ""));
+ if (!filePath.startsWith(`${root}${sep}`)) {
+ response.writeHead(403, { "content-type": "text/plain; charset=utf-8" });
+ response.end("Forbidden");
+ return;
+ }
+ try {
+ const body = await readFile(filePath);
+ response.writeHead(200, { "content-type": contentTypes[extname(filePath)] || "application/octet-stream" });
+ response.end(body);
+ } catch (error) {
+ if (error.code === "ENOENT") {
+ response.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
+ response.end("Not found");
+ return;
+ }
+ throw error;
+ }
+}
diff --git a/enterprise-tooling/test/enterprise-core.test.js b/enterprise-tooling/test/enterprise-core.test.js
new file mode 100644
index 0000000..021bad0
--- /dev/null
+++ b/enterprise-tooling/test/enterprise-core.test.js
@@ -0,0 +1,37 @@
+import test from "node:test";
+import assert from "node:assert/strict";
+import { buildAdminDashboard, buildEnterprisePayload, buildExportPipeline, buildRestApiCatalog, createWebhookEvent } from "../src/enterprise-core.js";
+
+test("builds enterprise admin dashboard metrics", () => {
+ const dashboard = buildAdminDashboard();
+
+ assert.equal(dashboard.overview.projects, 3);
+ assert.ok(dashboard.overview.storageGb > 100);
+ assert.ok(dashboard.departmentMetrics.some((item) => item.department === "Oncology"));
+ assert.ok(dashboard.compliance.some((item) => item.status === "attention"));
+});
+
+test("exposes secure API catalog and enabled integrations", () => {
+ const catalog = buildRestApiCatalog();
+
+ assert.match(catalog.auth, /Bearer token/);
+ assert.ok(catalog.routes.includes("POST /api/enterprise/webhooks"));
+ assert.ok(catalog.integrations.some((integration) => integration.type === "lms"));
+});
+
+test("creates structured webhook events", () => {
+ const event = createWebhookEvent("project.published", "proj-organoid");
+
+ assert.equal(event.type, "project.published");
+ assert.ok(event.deliveryTargets.length >= 2);
+ assert.equal(event.payload.reproducibilityScore, 94);
+});
+
+test("builds export pipeline preserving scholarly metadata", () => {
+ const pipeline = buildExportPipeline({ projectId: "proj-organoid", target: "Zenodo", format: "JATS" });
+ const payload = buildEnterprisePayload();
+
+ assert.ok(pipeline.plugins.includes("DataCite DOI"));
+ assert.ok(pipeline.preservedMetadata.includes("ORCID"));
+ assert.equal(payload.exportPipeline.complianceStatus, "ready");
+});