Case study
TRC-LU TMS
Overview
[In development] A training management system for TRC-LU: employees log training activities and upload CA/WAP and post-activity reports, HR reviews, approves, and reports on them. First release complete, deployment pending.
Problem
Employee training at TRC-LU lived in a 116-worksheet Excel register: 835 staff-tab rows that contradict the 110 titled activities, no employee IDs, free-text participant cells, and dates mixed with text ranges. Certificates were reviewed by hand, and there was no credible basis for division training reports.
Approach
A three-role system built around how the training office actually works. Employees register through a stepped flow (classification, division, section, plantilla position), log activities, and upload versioned evidence files; HR approves or returns them with a reason and a decision email that persists its outcome for retry. HR also manages plantilla position references with per-capacity required counts and a shared catalog of training titles and providers. Historical reports snapshot the organization at participation time so later reorganizations never rewrite the past.
Architecture
A single-file Hono API on Cloudflare Workers (69 routes, 2,613 lines) fronts Turso via its compat client and private R2 attachments: magic-byte sniffing, a 5 MB cap, versioned re-uploads after a return, and a compensating R2 delete when the metadata write fails. CSRF middleware validates origin, Sec-Fetch-Site, and content type on every mutation; sessions are opaque tokens with only a SHA-256 hash stored, 30-day idle and 90-day absolute expiry; passwords run Argon2id in WASM; rate limits are DB-backed sliding windows keyed by HMAC-ed account and IP that fail closed when unconfigured. The client attaches an idempotency key to every mutation.
Challenges
Argon2id costs about 48ms locally against Workers' ~10ms CPU budget, and the recorded decision was not to weaken password storage just to fit a free tier. The legacy workbook needs an HR-verified identity-matching and dedupe pass before a single row enters the staging database. Rate-limit windows stay deliberately unspecified behind a fail-closed gate rather than guessed production numbers.
Results
First release implemented: a 152-assertion integration pass over auth and user flows, and 10 migrations reconciling 27 sections, 330 capacity-specific positions, and 569 required-count mappings with zero foreign-key violations. Production deployment is the next milestone.