Skip to content

// project

SINC-P

A statutory student-grievance redressal system, built to survive a UGC inspection rather than a demo.

The brief

SINC-P rebuilds a 2019 final-year project from scratch: a statutory grievance-redressal system an Indian institution can put in front of a UGC inspector, with a clock on every case and a record nobody can quietly edit. Nothing from 2019 survived the rewrite, not the code, the schema, or the passwords, because almost every line of the original was an ordinary mistake (string-built SQL, unsalted md5, no ownership check on a grievance read) that is still running at real institutions today.

What shipped

  • A statutory SLA clock escalating officer, then admin, then the Ombudsperson tier the regulations require, plus a hash-chained audit trail a retro-edited remark cannot pass unnoticed.
  • Tenant isolation across four independent layers, down to Postgres row-level security, verified by a script that tries to break it rather than trusted from the application side alone.
Next.js 16React 19TypeScript strictPostgresDrizzle ORMTailwind v4Vitest
GitHub Doori (sibling KMP app) Active · AGPL-3.0 · UGC 2023-compliant

no public push in the last 20 events

// case study

The short version

The problem

The 2019 final-year project it replaces ran string-built SQL, unsalted md5 passwords and an IDOR that let any student read another student's grievance by counting upward through the URL. A compliance system that has to survive a UGC inspection cannot carry forward any of that.

The decision

Rebuild from scratch rather than patch it: parameterised queries under Postgres row-level security, scrypt with a per-password salt, an explicit authorization check on every read path, and a hash-chained event log where a retro-edited remark breaks verification at a nameable sequence number, enforced by a database trigger and a revoked privilege, not good intentions.

The result

Four independent tenant-isolation layers, including row-level security enforced even against table owners, verified by a script that stands up a throwaway Postgres and actively tries to break every layer rather than trusting isolation from the application side alone.

Evidence: Four independent tenant-isolation layers, including row-level security enforced even against table owners, verified by a script that stands up a throwaway Postgres and actively tries to break every layer rather than trusting isolation from the application side alone.
Go deeper

Analyst view: SINC-P draws on 7 stack entries and links to 1 outside source.

// design notes

How it works

A statutory clock that survives an audit

Every grievance carries a due date computed from the category override or the institution default, in Asia/Kolkata, in calendar or working days. The timezone handling is deliberate: IST sits at a fixed +5:30, so a due date computed off UTC calendar days lands a day early or late depending on what time somebody filed, and that is the kind of drift an inspection finds, not a test. Breaches escalate officer, then admin, then the Ombudsperson tier the UGC regulations require.

An append-only trail that shows its teeth

The grievance event log is hash-chained: each event commits to the one before it, so a retro-edited remark or a deleted escalation breaks verification at a nameable sequence number, enforced by a database trigger and a revoked privilege rather than good intentions. The UI calls this tamper-evident, not tamper-proof, because overselling it is the one lie an auditor could catch.

Tenant isolation you can attack

Four independent layers assume any one of them will eventually have a bug: application-level scoping, a transaction-local tenant context, Postgres row-level security enforced even against table owners, and a runtime database role that is neither owner nor superuser. A verification script stands up a throwaway Postgres and actively tries to break every layer rather than asserting isolation from the application side alone.

What changed since 2019

The 2019 tree ran unauthenticated SQL injection (string-concatenated queries), unsalted md5 passwords, an IDOR that let any logged-in student read every other student's grievance by counting upward through the URL, and an upload path that would execute an uploaded PHP file as a shell. The 2026 rewrite replaces each with parameterised queries under RLS, scrypt with a per-password salt, an explicit authorization check on every read path, and magic-byte-sniffed uploads capped in flight and stored outside the web root.

Published transparency, no login required

A public transparency page shows median days to resolution per category, with any figure computed from a small handful of cases suppressed at the query layer so a single-digit count in one department can never read as a name. The buyer (a Registrar or Dean of Student Welfare) gets the audit trail; students get a public scoreboard, which is what makes them actually file into the system the compliance record depends on.

// architecture

How it's built

Tenant isolation: four layers, any one assumed to fail

// under the hood

Tech stack

Framework

  • Next.js 16 (App Router)
  • React 19
  • Server Components

Data

  • Postgres
  • Drizzle ORM
  • Row-Level Security

Language & validation

  • TypeScript strict
  • noUncheckedIndexedAccess
  • Zod v4

Auth & ops

  • scrypt (node:crypto)
  • server sessions
  • Docker Compose deploy

Quality

  • Vitest
  • integration tests against a real Postgres

// gallery

Screens (13)

Swipe or use the arrows · tap a screen to enlarge

Follow the connections

From this build, go deeper.

See how everything connects →