Case 02Operations and safety

Kippis

Workplace safety in the pocket of the people on site.

A workplace safety platform that puts risk assessments, work permits, inspections and disciplinary warnings on the technician’s phone and ties them to employees, roles, safety standards, exams and PPE. Every night, five routines sweep each company’s legal deadlines and alert the right person ahead of the due date and again on the day, before the shift starts.

Client
Large construction company
Period
2025 — 2026
Team
4-person squad
Role
Full stack engineer · lead for the mobile app and safety rules
Stack
Go · Fiber · PostgreSQL · Expo · React Native · TypeScript
Confidentiality
Client data kept private
02

Results in numbers

  • 5legal deadlines watched every nighttraining, approval number, PPE lifespan, periodic exam and assets
  • 180endpoints in the multi-tenant API27 API modules, from HR and health to inventory and comms
  • 2,500employees trackedin-house and contracted, across several sites
  • 42unhealthy and hazardous conditions mapped24 under NR-15 and 18 under NR-16, with each role’s allowance rate
03

The business context

Client
Large construction company
Period
2025 — 2026

At a large construction company, workplace safety is both a legal requirement and a matter of life and death. Every site mixes in-house and contracted crews; every role carries safety standards, occupational health exams, PPE with a valid approval certificate and hazard or unhealthy-work allowances. Every critical activity starts with a risk assessment and a signed permit. When all of that lives on paper, spreadsheets and chat groups, an expired training shows up during an inspection, an audit or, at worst, after an accident.

04

The challenge

Records had to happen on site, in minutes, signed by the people who will do the job. HR, occupational health, safety and the warehouse needed to see the same employee without mixing data across companies. And deadlines of very different kinds — training certificates, PPE approval numbers, the lifespan of each unit, periodic medical exams and assets — had to become alerts for the right person, at the right time and never repeated, without burying the safety technician in notifications.

  1. Field first

    Risk assessments with a risk matrix, work permits and inspections filled in on the phone, step by step, with on-screen signatures and validation before sending. No permit may exist without a linked risk assessment.

  2. Every standard has its own rule

    NR-35, NR-15, NR-16, NR-06 — work at height, unhealthy work, hazardous work, PPE: each regulation demands different things from roles, classes and permits. Adding a new standard or following a revision had to be data entry, never a deploy.

  3. Deadlines that cannot slip

    Training, approval numbers, per-unit PPE lifespan, periodic medical exams and assets expire at different paces — and each one has a different person who must be warned.

05

What I delivered

I built the field app in Expo and React Native and structured the React web dashboard. Behind them, a Go and Fiber API with 180 endpoints across 27 API modules and 32 permission modules scopes every company at the session and treats standards, exams, PPE and allowances as data tied to the role. A Go scheduler runs five routines per company every night, works out who must be warned and records every delivery.

  1. Field app

    Five-step risk assessment with a 5 × 4 risk matrix, permits with crew and signatures, checklist inspections and warnings with witnesses and photos.

  2. Multi-tenant API

    Go and Fiber with the company taken from the session, three-level RBAC per module and per platform, MFA and an audit trail on every change.

  3. Standards, roles and training

    A standards catalog linked to classes and permits; each role defines exams, PPE and allowances from 42 mapped unhealthy and hazardous conditions.

  4. Expiry scheduler

    Five routines staggered between 3 and 5 a.m. that alert ahead of the due date and again on the day, with recipients resolved through the hierarchy and a single alert per item per day.

06

How it works

Follow the work through the system step by step, or click any component on the board to see what it does.

12 components · 13 connections · 5 layers

Click a component to see details←→to navigateWalk through the flow below

INPUTField appExpo / ReactNativeINPUTManagementdashboardReact / ViteSERVICETraining andstandardsGoSERVICEMulti-tenant APIGo / FiberSERVICESafety and healthGoSERVICEPPE and inventoryGoDATABASECertificates andevidenceAWS S3DATABASEOperationaldatabasePostgreSQLEXTERNALCA-EPI serviceHTTPSERVICEExpiry schedulerGo / cronQUEUEAlertsSMTP · inboxOUTPUTPending work andaction

drag to see the flow

Legend

  • Synchronous
  • Asynchronous
  • Secure channel
  • External integration
Text description of the flow
  1. From Field app to Multi-tenant API — Field entries (synchronous).
  2. From Management dashboard to Multi-tenant API — Management (synchronous).
  3. From Multi-tenant API to Training and standards — Classes (synchronous).
  4. From Multi-tenant API to Safety and health — Permits (synchronous).
  5. From Multi-tenant API to PPE and inventory — PPE, assets (synchronous).
  6. From Training and standards to Certificates and evidence — Certificate (synchronous).
  7. From Training and standards to Operational database — Attendance (synchronous).
  8. From Safety and health to Operational database — Persists (synchronous).
  9. From PPE and inventory to Operational database — Stock (synchronous).
  10. From PPE and inventory to CA-EPI service — CA lookup (external integration).
  11. From Operational database to Expiry scheduler — Due dates (asynchronous).
  12. From Expiry scheduler to Alerts — Pending items (asynchronous).
  13. From Alerts to Pending work and action — Alert (asynchronous).

Walk through the flow

←→change step

6 steps

From start to finish

Pick a step or press Play to follow the whole flow. The board highlights where the work goes as you move along.

07

System components

  1. Field app

    What it solves
    Bring safety records to the place where the work happens.
    How it works
    Eighteen screens on Expo Router. The risk assessment follows five steps — details, risks, activity steps, severity and signature — and each risk gets a frequency and severity on a 5 × 4 matrix that returns a priority from 1 to 5, with an administrative, PPE or engineering control. React Hook Form and Zod block incomplete submissions. The session lives in cookies in SecureStore, and the app only returns to login when the API answers SESSION_EXPIRED: a generic 401 never kills a form in progress.

    Expo · React Native · TypeScript · Zod

  2. Multi-tenant API

    What it solves
    Let HR, occupational health, safety and the warehouse share context without mixing companies.
    How it works
    The authentication middleware takes company and user from the session; the authorization middleware compares the level the route requires — view, interact or manage — with the role’s level for that module and platform, cached for 30 seconds and invalidated when the role changes. Login with an MFA code, a 1-hour session renewed by a 30-day refresh token and at most 5 web sessions per user. Sixty-eight audit points record who changed what. Seventy-eight tables across 6 schemas: core, hr, health, safety, stock and comms.

    Go · Fiber · PostgreSQL · JWT

  3. Standards, roles and occupational health

    What it solves
    Make the system adapt to each standard without rewriting code.
    How it works
    Each standard is a record with its activity and objective; link tables connect it to the classes that cover it and the permits that require it. The role defines mandatory exams, required PPE and allowances: unhealthy-work allowance under NR-15 at 10, 20 or 40%, hazard pay under NR-16 at a fixed 30%, and strenuous-work conditions. Medical certificates — pre-hire, periodic, return to work, exit and the health program — record fit, unfit or restricted.

    Go · PostgreSQL

  4. Training and certificates

    What it solves
    Record who attended, which standards the class covers and what evidence was issued.
    How it works
    Training classes or safety talks, typed as mandatory, specific or refresher, with instructor, workload, attendance and standards. The backend generates an A4 PDF certificate with no external dependency — name, ID, workload, instructor and expiry — and stores it in S3 per class and participant, ready to download at audit time.

    Go · PostgreSQL · AWS S3

  5. PPE, inventory and assets

    What it solves
    Know which equipment is with whom, whether its approval is valid and when it must be replaced.
    How it works
    The PPE model looks up the approval number on the CA-EPI service and stores its expiry. Each unit has a serial number, status and purchase date, from which lifespan is computed in days, weeks, months or years. Handouts to employees record the return and the item’s condition; tools and machines get loans with checklists, interventions and inventory. This module alone serves 38 endpoints.

    Go · PostgreSQL · CA-EPI

  6. Expiry scheduler

    What it solves
    Turn deadlines scattered across modules into actionable alerts for the right person.
    How it works
    A Go service separate from the API, with five jobs at their own times: training at 3:00, approval numbers at 3:30, PPE lifespan at 4:00, periodic exams at 4:30 and assets at 5:00. All reuse a snapshot of companies cached for 6 hours. Each job resolves the recipient — the class site manager, the manager of the sector whose roles use that PPE, the employee’s direct leader with fallback to the sector and the company — and stores the item, recipients and delivery result. Every deadline triggers one alert ahead of the due date and another on the day itself, and the query skips anything already alerted that day.

    Go · robfig/cron · SMTP

  7. Management dashboard

    What it solves
    Give safety management an administrative view of employees, PPE, inspections, training and permits.
    How it works
    React 19, Vite and Tailwind. The menu comes from the session: the backend returns the paths unlocked for the role from a registry of 32 permission modules, so changing a route never means rewriting profiles. Alert screens for approval numbers, lifespan, medical exams and training gather pending items, and the same alerts land in an inbox with an unread counter.

    React · Vite · TypeScript · Tailwind CSS

08

Decisions that hold the system up

  1. Company comes from the session, never the client

    The middleware loads the company on every request and every query filters by it. When a permit is created, risk assessments, technician, workers and signers are batch-validated against the same company — no screen can mix in another company’s data, even with a tampered parameter.

  2. A standard is data, not code

    A standard is a row in the catalog, linked to classes, permits and roles through relation tables. Supporting a new standard, a revision or a site-specific requirement becomes data entry, and the permit already shows which standards each worker is trained on.

  3. Permission by module, level and platform

    The same route can require different permissions on web and mobile: creating a risk assessment in the dashboard needs a management module, on the phone it needs the field module. The technician works on site without gaining admin access.

  4. Alerts recorded, not just fired

    Each delivery stores the item, recipients, where the owner came from and success. That prevents repeating the same notification on the same day and proves, at audit time, that the owner was warned in time.

09

Business results

  • Training, approval numbers, PPE, medical exams and assets alert their owner ahead of the due date and again on the day, before the shift, instead of surfacing at audit time.

  • Issuing a work permit on site dropped from about 20 minutes on paper to under 5 in the app.

  • Every permit is born linked to a risk assessment scored risk by risk, with crew and signers validated inside the same company.

  • Every alert is stored with its recipient and delivery result: when inspectors arrive, proof that the owner was warned is already there.

10

My role

Role
Full stack engineer · lead for the mobile app and safety rules
Team
4-person squad

I planned and built the mobile app from scratch, from route architecture to session handling, and did the real structuring of the web dashboard. In the API, I modeled the backend to support many safety standards and adapt to each one: standard, role, exam, PPE and allowance became related data, not rules hard-coded in the system. I wrote the business logic for exam alerts, PPE expiry and training notices, and turned the client safety team’s requirements into system validations.

What I owned

  • Built the Expo/React Native app: risk assessments, permits, inspections, warnings, MFA and password recovery across 18 screens.
  • Modeled standards, roles, exams, PPE and allowances as related data, so a new standard is data entry rather than code.
  • Designed RBAC with view, interact and manage levels per module, enforced separately for web and mobile.
  • Wrote the five expiry routines and recipient resolution through direct leader, sector manager and site manager.
  • Integrated PPE registration with the CA-EPI service and per-unit lifespan with inventory control.
  • Generated PDF training certificates in the backend itself, stored in S3 per class and participant.
11

Stack by layer

  • Mobile

    • Expo
    • React Native
    • Expo Router
    • React Query
    • React Hook Form
    • Zod
    • SecureStore
  • Web

    • React
    • Vite
    • TypeScript
    • Tailwind CSS
    • Zustand
  • Backend

    • Go
    • Fiber
    • sqlx
    • JWT
    • TOTP
    • robfig/cron
  • Data and integrations

    • PostgreSQL
    • Goose
    • AWS S3
    • CA-EPI
    • SMTP
  • Infra

    • Docker
    • Nginx
    • GitHub Actions
12

What I take from this project

  1. Modeling standard, role and requirement as related data from the start is what let the system follow each standard without a queue of code changes.

  2. A useful alert reaches the right person at the right time, never repeated. Resolving the recipient through the hierarchy and recording every delivery made the technician trust the system instead of muting it.

  3. In the field, an expired session must be an explicit UI state. Telling SESSION_EXPIRED apart from other 401s prevented lost forms and orphaned signatures.

Next case 03 LeadSpector From 300,000 companies to the right pitch, with AI citing its sources.

What’s the next system your business needs?

Tell me the context: a problem, an idea or a system that needs to evolve. I’ll reply with a clear technical path and the first step to get it built.