{"id":49556518,"url":"https://github.com/safetymp/autonomous-ehs-management","last_synced_at":"2026-07-12T06:00:33.676Z","repository":{"id":355320442,"uuid":"1227637140","full_name":"SafetyMP/Autonomous-EHS-Management","owner":"SafetyMP","description":"Web console for safety teams: incidents, CAPA plans, metrics, documents, training, audits—role-aware, PostgreSQL-backed.","archived":false,"fork":false,"pushed_at":"2026-07-05T17:53:02.000Z","size":2584,"stargazers_count":1,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T19:21:15.343Z","etag":null,"topics":["better-auth","capa","compliance","docker","drizzle-orm","ehs","environmental-health-and-safety","incident-management","iso-45001","kubernetes","nextjs","occupational-health","open-source","playwright-testing","postgresql","react","safety-management","self-hosted","trpc","typescript"],"latest_commit_sha":null,"homepage":"https://autonomous-ehs-management.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SafetyMP.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":"GOVERNANCE.md","roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-03T00:36:19.000Z","updated_at":"2026-07-05T17:53:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/SafetyMP/Autonomous-EHS-Management","commit_stats":null,"previous_names":["safetymp/autonomous-ehs-management"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/SafetyMP/Autonomous-EHS-Management","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SafetyMP%2FAutonomous-EHS-Management","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SafetyMP%2FAutonomous-EHS-Management/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SafetyMP%2FAutonomous-EHS-Management/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SafetyMP%2FAutonomous-EHS-Management/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SafetyMP","download_url":"https://codeload.github.com/SafetyMP/Autonomous-EHS-Management/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SafetyMP%2FAutonomous-EHS-Management/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35383520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-12T02:00:06.386Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["better-auth","capa","compliance","docker","drizzle-orm","ehs","environmental-health-and-safety","incident-management","iso-45001","kubernetes","nextjs","occupational-health","open-source","playwright-testing","postgresql","react","safety-management","self-hosted","trpc","typescript"],"created_at":"2026-05-03T03:04:45.981Z","updated_at":"2026-07-12T06:00:33.644Z","avatar_url":"https://github.com/SafetyMP.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Autonomous EHS Management System\n\n[![CI](https://github.com/SafetyMP/Autonomous-EHS-Management/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/SafetyMP/Autonomous-EHS-Management/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/SafetyMP/Autonomous-EHS-Management/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/SafetyMP/Autonomous-EHS-Management/actions/workflows/codeql-analysis.yml)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/SafetyMP/Autonomous-EHS-Management/badge)](https://scorecard.dev/viewer/?uri=github.com/SafetyMP/Autonomous-EHS-Management)\n[![License: Apache-2.0](https://img.shields.io/github/license/SafetyMP/Autonomous-EHS-Management)](LICENSE)\n[![Release](https://img.shields.io/github/v/release/SafetyMP/Autonomous-EHS-Management)](https://github.com/SafetyMP/Autonomous-EHS-Management/releases)\n\n**Autonomous compliance operations platform** — give your team one place to log what happened, assign fixes, and prove the follow-up.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/demo.gif\" alt=\"Autonomous EHS demo — command center, incidents, and CAPA register (synthetic demo data)\" width=\"900\" /\u003e\n\u003c/p\u003e\n\n## Screenshots\n\n| Command center | Incidents | CAPA register |\n|----------------|-----------|---------------|\n| ![Command center](docs/assets/command-center.png) | ![Incidents](docs/assets/incidents.png) | ![CAPA register](docs/assets/capa.png) |\n\n| Approvals | Tasks \u0026 reviews | Inspections |\n|-----------|-----------------|-------------|\n| ![Approvals](docs/assets/approvals.png) | ![Tasks \u0026 reviews](docs/assets/tasks.png) | ![Inspections](docs/assets/inspections.png) |\n\n| Metrics | Audit trail | Observations | Work permits |\n|---------|-------------|--------------|--------------|\n| ![Metrics](docs/assets/analytics.png) | ![Audit trail](docs/assets/audit-trail.png) | ![Observations](docs/assets/observations.png) | ![Work permits](docs/assets/permits.png) |\n\n- **Live demo:** [autonomous-ehs-management.vercel.app](https://autonomous-ehs-management.vercel.app) — or run locally in ~5 minutes ([Turnkey local demo](#turnkey-local-demo-docker-postgres))\n- **License:** [Apache 2.0](LICENSE) · **Docs:** [docs/README.md](docs/README.md) · **Contribute:** [CONTRIBUTING.md](CONTRIBUTING.md)\n- **OSS health:** [OpenSSF badge setup](REPO_SETUP.md#10-oss-health-badges--social-preview) · [OpenSSF Scorecard](.github/workflows/scorecard.yml) · social preview source in [`docs/assets/github-social-preview.png`](docs/assets/github-social-preview.png)\n\n## Contents\n\n- [Quick start](#quick-start)\n- [Environment snapshots](#environment-snapshots)\n- [Turnkey local demo (Docker Postgres)](#turnkey-local-demo-docker-postgres)\n- [GitHub Codespaces / Dev Container](#github-codespaces--dev-container)\n- [Non-demo development](#non-demo-development-existing-flow)\n- [Architecture (high level)](#architecture-high-level)\n- [Deploy](#deploy)\n- [What “autonomous” means here](#what-autonomous-means-here)\n- [Who this is for](#who-this-is-for)\n- [Open source, maintained for the long term](#open-source-maintained-for-the-long-term)\n- [Operators and integrations (self-host)](#operators-and-integrations-self-host)\n- [Enterprise SSO (OIDC pilot)](#enterprise-sso-oidc-pilot)\n- [License](#license)\n\n## Quick start\n\n1. **Clone and install:** `git clone \u003crepo-url\u003e \u0026\u0026 cd \u003crepo-dir\u003e \u0026\u0026 npm ci`  \n   **Path tip:** Avoid a **working directory name with a trailing space** (some macOS / archive flows introduce `\"…System \"`). That can break scripts, tooling, and shell `cd` unless you always **quote paths**—prefer renaming the folder to remove stray spaces.\n2. **Configure:** pick a row in **Environment snapshots** below and copy the listed template to `.env.local` (fill secrets).\n3. **Run:**\n   - **Docker demo:** `npm run demo:up` then `npm run dev` → [http://localhost:3000/sign-in](http://localhost:3000/sign-in) (details in [Turnkey local demo](#turnkey-local-demo-docker-postgres)).\n   - **Your own Postgres:** `npm run db:migrate` then `SEED_ADMIN_EMAIL=you@company.com npm run db:seed` then `npm run dev`.\n4. **Before a PR:** `npm run verify` — same bar as CI ([AGENTS.md](AGENTS.md)).\n\n## Environment snapshots\n\n| Profile | Start from | Required / typical |\n|--------|------------|-------------------|\n| **Local demo (Docker)** | [`.env.demo.example`](.env.demo.example) | `DATABASE_URL` on host **port 5433**, `DATABASE_USE_PG=1`, `BETTER_AUTH_SECRET` (32+ chars), `BETTER_AUTH_URL` / `NEXT_PUBLIC_APP_URL`, demo mode + admin credentials — **never enable demo flags in production.** |\n| **Local / hosted Postgres** | [`.env.example`](.env.example) | `DATABASE_URL`, `BETTER_AUTH_SECRET`, URLs; omit `DATABASE_USE_PG` when using Neon serverless; optional OIDC, `CRON_SECRET`, AI gateway, Upstash — see file comments. |\n| **CI (GitHub Actions / Vitest)** | [`.env.ci`](.env.ci) | Fixture `DATABASE_URL` + `DATABASE_USE_PG=1`, synthetic secrets; **not for real deployments.** |\n\nOptional **proposal-only AI** (when enabled) suggests intake wording or retrieves policy excerpts—it does **not** auto-submit incidents, approve CAPAs, or change regulated status. See [docs/ai-governed-intake.md](docs/ai-governed-intake.md).\n\n**Architecture \u0026 diligence:** [docs/README.md](docs/README.md) (full documentation index), [docs/architecture-map.md](docs/architecture-map.md) (system map), [docs/workflow-depth.md](docs/workflow-depth.md) (state machines + audit patterns), [docs/procurement-readiness.md](docs/procurement-readiness.md) (ROI / pilot / positioning workbook), [docs/approval-workflow.md](docs/approval-workflow.md) (CAPA approval gate), [docs/case-studies/pilot-template.md](docs/case-studies/pilot-template.md), [docs/module-maturity.md](docs/module-maturity.md) (feature maturity), [docs/operational-webhooks.md](docs/operational-webhooks.md) (outbound event delivery), [docs/roadmap/hris-portco-integration-playbook.md](docs/roadmap/hris-portco-integration-playbook.md) (SCIM / HRIS identity), [docs/roadmap/action-queue-dashboard-spec.md](docs/roadmap/action-queue-dashboard-spec.md) (unified **Your work** queue), [docs/codebase-layout.md](docs/codebase-layout.md) (`src/` directory guide).\n\n**Console navigation:** field steps and routes in [`docs/user-manual-ehs-console.md`](docs/user-manual-ehs-console.md), including **Records \u0026 metrics → Incidence rates** (TRIR-style analytics) and the **Your work** action queue on command center / task hub. The **authoritative sidebar structure** in code is [`src/lib/dashboard-nav-links.ts`](src/lib/dashboard-nav-links.ts) (`DASHBOARD_NAV_SECTIONS`).\n\n**Contributors \u0026 agents:** [AGENTS.md](AGENTS.md) (verify / CI), [GOVERNANCE.md](GOVERNANCE.md) (evergreen OSS), [docs/README.md](docs/README.md) (documentation index), [docs/codebase-layout.md](docs/codebase-layout.md) (`src/` map), [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md), [CONTEXT.md](CONTEXT.md) (architecture), [COMPLIANCE.md](COMPLIANCE.md) (regulatory / data governance).\n\n---\n\n## Turnkey local demo (Docker Postgres)\n\n**Goal:** Postgres + migrations + realistic demo data + optional “Try demo admin” on sign-in.\n\n### 1. Start the database\n\n```bash\ndocker compose -f docker-compose.demo.yml up -d\n```\n\nWait until Postgres is healthy (`pg_isready`).\n\nThe compose file maps the database to **host port `5433`** so it does not fight with a local Postgres that already listens on `5432`. Your `DATABASE_URL` must use that port (see `.env.demo.example`).\n\n### 2. Environment\n\n```bash\ncp .env.demo.example .env.local\n```\n\nEdit `.env.local` if needed. Important:\n\n- **`DATABASE_USE_PG=1`** when using the compose Postgres URL (uses the `pg` pool instead of the Neon serverless driver).\n- **`BETTER_AUTH_SECRET`**: at least 32 characters (use a random value outside committed examples).\n- **`DEMO_MODE=true`** and **`NEXT_PUBLIC_DEMO_MODE=1`** to enable server-side demo sign-in and the sign-in CTA.\n- **`DEMO_ADMIN_EMAIL`** / **`DEMO_ADMIN_PASSWORD`**: credentials created by the demo seed (must match what you intend to use).\n\n**Security:** Never set `DEMO_MODE` on production. If demo secrets leak, rotate them.\n\n### 3. Install, migrate, seed, run\n\n**One command (after `.env.local` exists):** brings up compose, waits for Postgres, migrates, and seeds:\n\n```bash\nnpm ci\nnpm run demo:up\nnpm run dev\n```\n\n**Or step-by-step:**\n\n```bash\nnpm ci\nnpm run db:migrate\nnpm run db:seed:demo\nnpm run dev\n```\n\n`npm run db:migrate` runs [`scripts/migrate.ts`](scripts/migrate.ts) so database errors are printed clearly (wrapper around Drizzle’s migrator).\n\n**Demo data after upgrades:** `npm run db:seed:demo` is **idempotent per domain**—re-run it to **backfill** new fixtures (observations, permits, inspections, approvals, environment, contractors, privacy DSAR sample, program CB audit/certificate, etc.) without wiping the org. For a **clean slate** on the throwaway **Demo Organization**, use the reset script below.\n\n**Evaluator-oriented fixtures** (idempotent `[Demo]` scope): **environmental regulatory permits** (conditions, monitoring link, one pending with approval inbox), **incident RCA** (5 Whys + Ishikawa on seeded incidents; showcase bow-tie/chronology where applicable), **risk** register with overdue review + a **task-based** assessment and **steps**, **Command Center** onboarding checklist rows marked complete when prerequisites exist, **program automation** samples (`escalation_event`), **integrations** backlog (**failed** `integration_event`, disabled operational webhook + connector mapping JSON), **RAG** (`rag_source` + `rag_chunk` without embeddings — list/detail friendly), **obligation ↔ RAG** link on the stormwater obligation, and an **evidence attachment** registry row with a **non-fetchable** placeholder URI. **Context Sync** artifacts are **not** seeded. The **workflow catalog** under `/dashboard/workflow-catalog` is **code-derived** ([`src/lib/workflow/catalog.ts`](src/lib/workflow/catalog.ts)), not database fixtures.\n\n**Cron telemetry note:** seed can insert **synthetic `cron_job_run`** rows for `reminders` and `data-retention` when there is no successful run in the last 7 days — the table is **deployment-global** (not org-scoped). Use only on **throwaway** demo databases; set **`DEMO_SEED_CRON_RUNS=0`** in `.env.local` to skip, or keep shared staging operator telemetry untouched.\n\nOpen [http://localhost:3000/sign-in](http://localhost:3000/sign-in). Use **Try demo admin** (when enabled) or sign in with the demo email and password from `.env.local`.\n\n**Health check:** [http://localhost:3000/api/health](http://localhost:3000/api/health) returns JSON `{ ok, database }` after the app can reach Postgres.\n\n### Browse-only sandbox\n\nSet **`DEMO_READ_ONLY=true`** (with **`DEMO_MODE=true`**). All **tRPC mutations** return `FORBIDDEN`; reads still work so stakeholders can explore safely.\n\n### Reset demo organization (clean slate)\n\n```bash\nnpm run db:seed:demo:reset\n```\n\nClears **`[Demo]`-prefixed** rows for **Demo Organization** across field operations, demo-scoped **approval requests** (CAPA, **work permit**, and **environmental regulatory permit** pipelines), **escalation_event** backlog for that org, **integration** rows with `demo.*` event types, demo **RAG** sources/chunks + obligation evidence links, **evidence attachments** with demo filenames or placeholder URIs, **operational_webhook_endpoint** at the synthetic `example.invalid` URL, **integration_connector_mapping** rows flagged `demoFixture`, **risk_assessment_step** + assessments, environment (**regulatory permits**, conditions, obligation-linked monitoring cleared before aspects/obligations), planning, program (including KPI, CB audit, certificate), contractors, a **synthetic DSAR** privacy row, OSHA sidecar sample, incidents, CAPA, training, controlled documents, and internal audits—then runs `db:seed:demo` again. **Does not** delete global **`cron_job_run`**. Use only on throwaway demo DBs.\n\n### Incremental refresh (keep existing demo rows)\n\n```bash\nnpm run db:seed:demo\n```\n\nSkips domains that already have demo data and fills in anything missing (for example after pulling new dashboard features).\n\n### Troubleshooting\n\n| Issue | What to try |\n|--------|------------|\n| **`role \"ehs\" does not exist` on port 5432** | Your host `5432` is another Postgres. Use **`DATABASE_URL` … `@127.0.0.1:5433`** with the demo compose file (published as **5433**). |\n| **Migration errors / duplicate index** | Use a **fresh volume**: `docker compose -f docker-compose.demo.yml down -v`, then `up -d`, then `npm run db:migrate`. |\n| **`DEMO_MODE` on Vercel production** | Not supported: **`VERCEL_ENV=production`** fails startup if `DEMO_MODE=true` (see [`src/instrumentation.ts`](src/instrumentation.ts)). |\n| **Playwright demo login** | Set **`PLAYWRIGHT_DEMO=1`**, run **`npm run demo:up`** and **`npm run dev`** with demo `.env.local`, then `npx playwright test tests/e2e/demo` (includes optional **fixture richness** checks against seeded RCA, env permits, risk steps, and integration DLQ). |\n| **CSV import (`/dashboard/import`)** | Paste CSV with header row. **Environmental aspects:** `name,activity,description` then e.g. `Stormwater sampling,Monitoring,\"Quarterly outfall tests\"`. **Hazards:** `title,description` then e.g. `Forklift traffic,\"Blind corners near loading bay\"`. |\n\n### Optional: richer narratives during seed\n\nIf **`OPENAI_API_KEY`** (and optional **`OPENAI_BASE_URL`**) are set when you run `db:seed:demo`, incident and CAPA text is lightly rewritten for variety. Without a key, deterministic copy is used (CI-friendly).\n\n---\n\n## GitHub Codespaces / Dev Container\n\nThe repo includes [`.devcontainer/devcontainer.json`](.devcontainer/devcontainer.json) with Postgres (pgvector) + a **Node 22** dev container. Reopen the project in the container; **`postCreateCommand`** runs `npm ci`, `db:migrate`, and `db:seed:demo` (set secrets like `BETTER_AUTH_SECRET` in Codespace secrets if you override the compose defaults).\n\n---\n\n## Non-demo development (existing flow)\n\n1. Point **`DATABASE_URL`** at your Postgres (Neon or other).\n2. Do **not** set `DATABASE_USE_PG` (or set `DATABASE_USE_PG=0`) when using the Neon serverless connection string with the default driver.\n3. Set **`BETTER_AUTH_URL`** and **`NEXT_PUBLIC_APP_URL`** to your dev origin.\n4. Sign up once, then link RBAC:  \n   `SEED_ADMIN_EMAIL=you@company.com npm run db:seed`\n\nVerification (same bar as CI `verify` job):\n\n```bash\nnpm run verify          # eslint, tsc, vitest\nnpm run verify:all      # + Playwright smoke\nnpm run test:e2e:smoke  # smoke only\nnpm run screenshots     # README demo GIF (2s per frame; demo stack + dev server)\n```\n\n**Local Playwright smoke (signed-in flows):** CI always runs `@smoke` E2E against a service Postgres after **`npm run db:migrate`** and **`npm run db:seed:ci`**. On a developer machine, the same tests are **skipped** unless you set **`PLAYWRIGHT_E2E_EMAIL`** and **`PLAYWRIGHT_E2E_PASSWORD`** (see [`.env.example`](.env.example)) **and** use a migrated, seeded database. Flow coverage is listed in [AGENTS.md](AGENTS.md) (Smoke E2E table).\n\n**PortCo staging checklist:** after configuring integrations on a pilot org, run **`npm run portco:pilot-verify`** against your database — see [docs/qa/portco-staging-pilot.md](docs/qa/portco-staging-pilot.md).\n\n---\n\n## Architecture (high level)\n\n```mermaid\nflowchart LR\n  user[Browser]\n  subgraph edge [Next.js]\n    pages[App Router]\n    trpc[tRPC API]\n    auth[Better Auth]\n  end\n  pg[(PostgreSQL + pgvector)]\n  cron[Cron jobs]\n  user --\u003e pages\n  pages --\u003e trpc\n  pages --\u003e auth\n  trpc --\u003e pg\n  auth --\u003e pg\n  cron --\u003e trpc\n  cron --\u003e pg\n```\n\nDeeper maps: [docs/architecture-map.md](docs/architecture-map.md).\n\n| Layer | Technology |\n|--------|------------|\n| App \u0026 routing | **Next.js 16** (App Router), **React 19** |\n| API \u0026 data fetching | **tRPC 11**, **TanStack Query** |\n| Auth | **Better Auth** (email + password + optional OIDC via Generic OAuth), Drizzle adapter |\n| Database | **PostgreSQL**, **Drizzle ORM**; local demo uses **`pg`** (`DATABASE_USE_PG=1`), hosted uses **Neon serverless** driver by default |\n| AI / RAG | Optional **OpenAI-compatible** gateway; **pgvector** for embeddings |\n| Quality | **ESLint**, **Vitest**, **Playwright** smoke E2E |\n\n---\n\n## Deploy\n\n**Self-host (Docker / Kubernetes):** see **[`docs/self-host-quickstart.md`](docs/self-host-quickstart.md)** for image build, cluster rollout, cron jobs, optional pg-boss worker, and metrics scrape.\n\n**Canonical ship path:** staging and production stay **Git authoritative** (GitHub Actions, Vercel and/or EKS) — not IDE tool connections alone. Start with **[`docs/cursor-tool-connections-deployment.md`](docs/cursor-tool-connections-deployment.md)** (promotion workflow, Vercel + Neon preview notes, secured `/api/cron/*`) and **[`REPO_SETUP.md`](REPO_SETUP.md)** (environments, secrets, optional OIDC). Implementation contracts: [`src/lib/env.ts`](src/lib/env.ts), [`vercel.ts`](vercel.ts) (platform HTTP crons), [`deploy/k8s/`](deploy/k8s/) (cluster manifests and examples). Use strong secrets, disable **all** demo flags, and run **`npm run verify`** before merge ([AGENTS.md](AGENTS.md)). Day-2 ops and runbooks: **[`.cursor/skills/devops-sre/SKILL.md`](.cursor/skills/devops-sre/SKILL.md)**.\n\n---\n\n## What “autonomous” means here\n\nIn this product, **autonomous** refers to **operations that keep moving without ad hoc spreadsheet chasing**: scheduled jobs (reminders, data retention, roster reconciliation, SLA checks), **recorded escalation events** when follow-ups or approvals breach deadlines, **integration failure triage** (failed inbound events with operator retry—not silent drops), durable **field outbox replay**, **integration ingest**, and **incidence-rate analytics (TRIR-style)** from IMS recordables and establishment hours—not a substitute for official OSHA filings—all with PostgreSQL as the auditable system of record. **Optional AI** suggests wording or retrieves policy context; it does **not** auto-close incidents, auto-approve CAPAs, or change regulated status without human action through normal, permission-gated workflows ([docs/ai-governed-intake.md](docs/ai-governed-intake.md), [docs/procurement-readiness.md](docs/procurement-readiness.md)).\n\n## Who this is for\n\nAutonomous EHS ships under **Apache 2.0** ([LICENSE](LICENSE)). You run, modify, and redistribute the software under those terms; production still needs your own ops, counsel, and compliance review ([COMPLIANCE.md](COMPLIANCE.md)).\n\n| Adopter | Typical goal | Start here |\n|---------|--------------|------------|\n| **Self-host IT / platform team** | Tenant-owned Postgres, predictable infra cost, data residency in your cloud account | [Self-host quickstart](docs/self-host-quickstart.md), [open-source TCO](docs/open-source-tco.md), [Deploy](#deploy) |\n| **Systems integrator (SI) / iPaaS partner** | Wire HRIS/LMS into canonical inbound envelopes without reading tRPC internals | [Integration inbound contract](docs/integration-inbound-contract.md), [connector mapping](docs/integration-connector-mapping.md) |\n| **PortCo / PE portfolio pilot** | Scoped IMS pilot (incidents → CAPA → evidence, contractor wedge, HRIS identity)—not a full suite rip-and-replace | [PortCo module value](docs/portco-module-value-assessment.md), [procurement readiness](docs/procurement-readiness.md) |\n| **Managed SaaS operator** | Vercel (or similar) + managed Postgres with the same application source | [Deploy](#deploy), [`.env.example`](.env.example), [`vercel.ts`](vercel.ts) |\n| **Contributors \u0026 evaluators** | Local demo, tests, or patches without a sales conversation | [Quick start](#quick-start), [CONTRIBUTING.md](CONTRIBUTING.md), [AGENTS.md](AGENTS.md) |\n\n**Open source \u0026 TCO:** [`docs/open-source-tco.md`](docs/open-source-tco.md) — license snapshot and illustrative self-host vs seat-priced inspection SaaS math.\n\n## Open source, maintained for the long term\n\nAutonomous EHS is an **evergreen open source project**: Apache 2.0, auditable **PostgreSQL + Drizzle** schema with migrations in-repo, and a public merge bar (`npm run verify`, same as CI). We optimize for **forkability, self-host longevity, and tenant-owned data**—not a single mandatory SaaS SKU.\n\n| Signal | Where to look |\n|--------|----------------|\n| **License \u0026 economics** | [LICENSE](LICENSE), [docs/open-source-tco.md](docs/open-source-tco.md) |\n| **Governance \u0026 maintainers** | [GOVERNANCE.md](GOVERNANCE.md), [`.github/CODEOWNERS`](.github/CODEOWNERS) |\n| **Honest product scope** | [docs/module-maturity.md](docs/module-maturity.md) |\n| **Direction \u0026 community** | [ROADMAP.md](ROADMAP.md), [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) |\n| **Security reporting** | [SECURITY.md](SECURITY.md) |\n\nProduction still requires **your** ops, backups, IAM, counsel, and compliance review ([COMPLIANCE.md](COMPLIANCE.md)). There is **no separate LTS branch** today—pin image digests or git SHAs and test upgrades in your environment ([GOVERNANCE.md](GOVERNANCE.md)).\n\n---\n\n## Operators and integrations (self-host)\n\nThese surfaces are primarily for **platform engineers**, **SRE**, and **governed tool access**—not day-to-day EHS Console menus. Operator UI for integrations, SCIM/OIDC JIT, roster drift, and failed-event triage lives at **`/dashboard/integrations`**.\n\n| Surface | Route / API | Doc link |\n|---------|-------------|----------|\n| Inbound integration | `POST /api/integration/inbound` | [integration-inbound-contract.md](docs/integration-inbound-contract.md) |\n| SCIM 2.0 | `/api/scim/v2/Users`, `/api/scim/v2/Groups` | [hris-portco-integration-playbook.md](docs/roadmap/hris-portco-integration-playbook.md) |\n| Operational outbound webhooks | tRPC + integrations UI | [operational-webhooks.md](docs/operational-webhooks.md) |\n| Context Sync (REST) | `/api/contextsync/*` | [runbooks/context-sync-provenance.md](docs/runbooks/context-sync-provenance.md) |\n\n- **Context Sync (REST):** Session-authenticated HTTP routes under **`/api/contextsync/*`** for **Context Sync**—IMS-linked artifacts, permissions, and provenance—when the organization enables the capability. Access is rate-limited like other gated APIs; optional caps: **`CONTEXT_SYNC_ORG_DAILY_READ_LIMIT`** and **`CONTEXT_SYNC_PROVENANCE_MAX_LIMIT`** (see [`src/lib/env.ts`](src/lib/env.ts), [`.env.example`](.env.example)). Architecture: [`docs/architecture-map.md`](docs/architecture-map.md).\n\n- **Scheduled HTTP crons:** [`vercel.ts`](vercel.ts) schedules three platform cron handlers (Bearer **`CRON_SECRET`**):\n\n  | Route | Schedule (UTC) | Purpose |\n  |-------|----------------|---------|\n  | `GET /api/cron/reminders` | `0 8 * * *` | SLA reminders, escalations, operational webhook dispatch |\n  | `GET /api/cron/data-retention` | `30 9 * * *` | Retention policy execution |\n  | `GET /api/cron/integration-roster-reconcile` | `0 3 * * *` | Nightly HRIS roster drift for orgs with snapshots (detect + manual reconcile) |\n\n  **`GET /api/cron/metrics`** is a separate **pull/scrape** endpoint (Prometheus or JSON)—it is **not** in the `vercel.ts` schedule; configure an external scraper or agent in Kubernetes or your observability stack. Runbook: [`docs/runbooks/cron-metrics-observability.md`](docs/runbooks/cron-metrics-observability.md).\n\n- **Background jobs:** When **`PG_BOSS_ENABLED=true`**, enqueue paths use **pg-boss** in Postgres; run a worker process with **`npm run job:worker`** alongside the web app. Details: [`docs/JOB_QUEUE.md`](docs/JOB_QUEUE.md).\n\n---\n\n## Enterprise SSO (OIDC pilot)\n\nWhen **`OIDC_DISCOVERY_URL`**, **`OIDC_CLIENT_ID`**, and **`OIDC_CLIENT_SECRET`** are all set (see [`src/lib/env.ts`](src/lib/env.ts)), the server registers Better Auth’s **Generic OAuth** plugin. Add to `.env.local`:\n\n- **`OIDC_PROVIDER_ID`** — defaults to `enterprise-oidc`; must match the client button (`authClient.signIn.oauth2`).\n- **`NEXT_PUBLIC_ENTERPRISE_SSO=1`** — shows **Continue with company SSO** on [`/sign-in`](http://localhost:3000/sign-in).\n- **`NEXT_PUBLIC_OIDC_PROVIDER_ID`** — optional override; defaults to `enterprise-oidc`.\n\nRegister the redirect URL with your IdP: `{BETTER_AUTH_URL}/api/auth/oauth2/callback/{OIDC_PROVIDER_ID}`.\n\n**Org linkage:** by default, first-time OIDC users still need membership/RBAC ([`scripts/seed.ts`](scripts/seed.ts) / admin invite flow). Optional **JIT**: set `OIDC_JIT_ENABLED=true`, `OIDC_JIT_DEFAULT_ORG_ID`, and optionally `OIDC_JIT_ROLE_SLUG` per [docs/OIDC_JIT_PROVISIONING.md](docs/OIDC_JIT_PROVISIONING.md) (review with IdP/counsel before production).\n\n---\n\n## License\n\nLicensed under **Apache License 2.0** — see **[LICENSE](LICENSE)**. SPDX: **`Apache-2.0`** (also set in **`package.json`**).\n\nContributing, governance, and security: **[CONTRIBUTING.md](CONTRIBUTING.md)**, **[GOVERNANCE.md](GOVERNANCE.md)**, **[SECURITY.md](SECURITY.md)**. GitHub org setup: **[REPO_SETUP.md](REPO_SETUP.md)**.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafetymp%2Fautonomous-ehs-management","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsafetymp%2Fautonomous-ehs-management","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafetymp%2Fautonomous-ehs-management/lists"}