app / safety-app
!

Not installable via adompkg

This app has no published release. adompkg install kyle/safety-app will not work until a maintainer publishes a tarball with install.sh and uninstall.sh.

See the publishing docs for the package.json schema and tarball layout required to ship this app.

Adom Safety & Health Program

A web app that runs the entire safety program for an Adom-style facility — policy reader, periodic-task tracker, incident reporting, OSHA recordkeeping, lockout/tagout register, hazard communication, and more — all backed by a SQLite database, a Google Chat reminder daemon, and an audit log of every mutating action.

Live URL

r1fapjfs5ruv.adom.cloud — auto-deployed from origin/main every 2 minutes via watchdog cron on a dedicated default-light service container (drew2-service-smokey).

What's in it

The app has 16 left-nav tabs, each handling one slice of the safety program:

  • Dashboard — open obligations, overdue tasks, open CAPAs, high risks, upcoming compliance
  • Policy — five OSHA-aligned sub-programs (main program + EAP + HazCom + LOTO + PPE), versioned on every edit. Internal toggle for the assembled binder-ready full document.
  • Periodic Tasks — recurring obligations with cadence in days. "Log completion" rolls next-due forward.
  • Incidents — auto-numbered INC-YYYY-####. Root cause, corrective actions, OSHA recordable flag, CSV export.
  • Corrective Actions — ISO 9001-style CAPA register sourced from incidents / inspections / risks / audits.
  • Risk Register — 5×5 likelihood × severity heat map with current and residual scoring.
  • Inspections — template-driven checklists. Pass/fail/N/A per item; failure auto-creates a 14-day-due CAPA.
  • Permits-to-work — hot work, confined space, electrical, line break. Approval chain + expiry reminders.
  • Chemical Exposures — per-employee × per-chemical ledger with 30-year retention per HazCom.
  • OSHA Forms — printable HTML for Form 300 / 300A / 301. Browser → File → Print → PDF.
  • Chemicals & SDS — inventory with GHS hazard pills. New chemicals trigger a PubChem auto-lookup (CAS / SDS / hazards) → admin gets a Google Chat ping with a "Review in app" button.
  • Equipment / LOTO — 29 CFR 1910.147. Energy sources, written procedure per machine, audit cadence.
  • PPE Assessments — per-work-area hazard assessment + required PPE. Annual review reminder.
  • Training Records — append-only log of who-was-trained-on-what + expiry.
  • Supplies & Ordering — vendor links per category, bound to the policy that requires each item.
  • Audit Log — every mutating action stored with actor + before/after JSON. Tamper-evident.

A QR-tag scan landing at /scan/eq/:id shows quick-action buttons for any equipment record.

What's special

Hourly Google Chat TODO digest. A single Card v2 message lists every item that needs attention right now: due tasks, open CAPAs, expiring permits, open incidents, chemicals missing CAS/SDS, equipment missing LOTO procedures, PPE assessments past review. The card is sent only when the list changes and at most once per hour (top of hour). Completing items in the app rolls into the next hourly digest — no immediate ping. The dedup state is a single reminder_digest row keyed on a SHA-256 hash of the canonical TODO set.

PubChem auto-lookup with Google Chat approval. Adding a new chemical triggers a fire-and-forget PubChem PUG-REST call that proposes CAS / IUPAC name / GHS signal word / H-codes / Sigma-Aldrich SDS URL. Admin gets a Google Chat ping with a "Review in app" button that lands them on a per-field accept/reject modal. Daily quota cap (80,000 req/day) and 5-req/sec pacer protect against tripping PubChem's published rate limits.

34-check Puppeteer e2e suite runs against the live URL — every route, every CRUD round-trip, OSHA exports, QR-scan API, audit-log capture. Asserts no JS console errors and no failed network requests.

Audit log captures everything. Every create / update / delete writes a row with actor email, action type, entity ID, IP, user-agent, request id, and full before-and-after JSON snapshots. Page versions are kept indefinitely for inspector review.

App-creator compliant UI. Dark theme always (#0d1117 page / #161b22 panels), Familjen Grotesk + Satoshi + JetBrains Mono fonts, monochrome white SVG icons, frosted-glass cards, hover-delayed tooltips on every button (500ms, body-appended position:fixed per human-ui-patterns §1d), GET /api/state + GET/POST /api/console + dev-gated POST /api/eval for AI drivability, fixed Adom top-header bar on every page.

Architecture

Service container (drew2-service-smokey, default-light)
├── Express + SQLite    safety-app server, port 8090, public proxy URL
├── reminder-daemon     ticks every 60s, fires hourly TODO digest to Google Chat
└── cron watchdog       pulls origin/main every 2 min and bounces processes on change

A .env file at /etc/safety-app.env holds the GChat webhook URL, public base URL, and (optional) SHUTDOWN_TOKEN.

Repo

https://github.com/adom-inc/safety-app (private, adom-inc org). Watch the demo walkthrough video for a 79-second tour of every left-nav tab.

Install on a fresh service container

# 1. Provision the container (only if you don't have one)
adom-cli carbon user repo-create service-safety
adom-cli carbon containers create --image-id 69b43b3e58d13e5ce628cdc5 \
  --repo-id <repo-id> --class small

# 2. SSH in and run the idempotent provisioner from the repo
ssh <slug>@adom.cloud
git clone [email protected]:adom-inc/safety-app.git /home/adom/safety-app
sudo tee /etc/safety-app.env > /dev/null <<EOF
GCHAT_WEBHOOK_URL='https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=...'
PUBLIC_BASE_URL='https://<port-mapping-host>.adom.cloud'
GCHAT_WEBHOOK_NAME='gchat-safety'
EOF
sudo chown adom:adom /etc/safety-app.env && sudo chmod 0600 /etc/safety-app.env
bash /home/adom/safety-app/scripts/provision-service-container.sh

# 3. Add a port mapping for 8090
adom-cli carbon containers port-add <slug> --port 8090

The watchdog cron pulls origin/main every 2 minutes, so future updates ship with git push.

Authored with

Claude Code (Opus 4.7, 1M-context).