Dev Jadiya

Colophon

How this site is built, and what it knows about you

Everything here is checkable. The stack is what the repository actually depends on, the limits are printed from the table the rate limiter reads, and the privacy list includes the answers that are not flattering.

The stack

devjadiya.com — stack

cat stack.txt

LayerWhatWhy
frameworkNext.js 16, App RouterStatic pages with incremental revalidation; server components by default.
languageTypeScript, strictNo implicit any anywhere, including the database layer.
databaseNeon Postgres + DrizzleServerless HTTP driver; the schema is typed from one file.
mediaCloudflare R2Free egress; every image transcoded to AVIF and WebP at four widths.
editorTiptapPosts are stored as a JSON document, not HTML, so nothing is ever injected.
stylingTailwind CSS v4CSS-first tokens; one accent, one type scale, no component library theme.
authAuth.js v5 + GitHubOne allowlisted account. There is no users table to attack.
hostingVercel, deployed from mainA push is the whole deploy procedure. No CLI, no dashboard step.
testsVitestAn offline unit suite and an opt-in one that hits the real services.

What is actually enforced

Security on a personal site is usually a paragraph. These are the controls in the code, each one a thing that refuses a request rather than a thing that hides a button.

devjadiya.com — controls

./audit --controls

authorisation Every admin mutation re-checks the session server-side before touching the database. The route guard is not the UI.

validation Every public write parses into a closed Zod schema. Unknown fields are a 400, not a shrug.

csp A content security policy that pins object-src, base-uri, form-action and frame-ancestors.

private files Resume PDFs are private objects. A short-lived signed URL is fetched server-side and streamed, so the signature never reaches a browser.

audit Every admin action and every sign-in - accepted or refused - writes a row I can read.

moderation Nothing a reader writes appears until I have read it. Turnstile and the rate limit only keep the queue usable.

The rate limits

Printed from the table the limiter reads, so this cannot drift away from what the site does. Counted per instance, which is the honest limitation of doing this without a shared store.

devjadiya.com — limits

./audit --limits

SurfaceAllowedWindow
login1010 min
adminMutation3001 min
adminUpload405 min
adminRead6001 min
comment55 min
interaction1201 min
track2401 min
resolver601 min
search1201 min
revalidate601 min

What this site knows about you

devjadiya.com — privacy

./audit --privacy

cookies for readersnone

third-party analyticsnone

ad or tracking scriptsnone

fontsself-hosted

your IP addresshashed with a salt that rotates daily, never stored raw

read countsa number per post, with nothing attached to it

link opensa timestamp and those daily hashes, so I know a share was read

The last line is the one worth saying plainly: when I share a resume or a short link, I can see that it was opened and roughly when. I cannot see who you are, and after a day the hash that grouped your visits is not reproducible even with the database in front of me.

Search

Search posts, projects, talks and more