This one is for engineers. It describes how Fledgy AI is built rather than how to use it, so it names libraries and internals throughout. If you are looking for instructions, everything else in the documentation is written for you instead.
| Layer | Choice |
|---|---|
| Framework | Next.js 16, App Router, Turbopack |
| UI | React 19.2 Server Components, TypeScript, Tailwind v4 |
| Database | node:sqlite: Node 24's built-in SQLite |
| Auth | Clerk hosted sign-in, mapped to a local users row |
pdf-lib, standard fonts only | |
| Validation | zod at action boundaries |
The whole application runs with no external services. No database server, no native modules to compile, no CDN, and the two web fonts are committed to the repo. npm install && npm run dev and it works.
A thin query layer in src/lib/db.ts exposes all, get and run. Two details matter:
node:sqlite returns null-prototype objects, which React Server Components refuse to serialise into Client Components. Fixing it once at the boundary means no caller ever has to think about it.CREATE TABLE IF NOT EXISTS cannot add a column to an existing table.Ownership is proven by the query, not by comparing a fetched row. Every scoped read and write includes the owning id in its WHERE clause, so a valid id belonging to someone else simply does not match. There is no separate permission check to forget.
Cross-account viewing, counselor and contributor, funnels through one shared rule in src/lib/access.ts that returns who the viewer is, how they reach the student, and what they may see. The PDF route, the counselor view and the contributor view all consume it, so they cannot disagree.
There is deliberately no middleware. Next.js 16 renamed middleware to proxy and it is the wrong place for auth; every route checks the session in its own layout and every server action re-checks independently.
All writes are Server Actions. Each one re-authenticates rather than trusting that the page rendered for the right person, and revalidates the paths it affects.
Multi-row money movement, marketplace purchases, is wrapped in an explicit BEGIN IMMEDIATE / COMMIT with rollback, because a partial write means a student pays for an essay they cannot read.
The public site is modelled on the premium admissions consultancies: a warm paper ground, one deep green that is ink, header, footer and button at once, a light serif for headlines and a plain grotesque for everything else, hairline rules, pill buttons that invert on hover, and generous space. The bright leaf green is the one accent — it underlines the phrase a headline is selling, colours a table's highlight column and the announcement bar, and is never used for text. The mark is drawn in the ink itself, so it is green on paper and white on a dark band.
Two faces, both self-hosted variable fonts in src/lib/fonts:
display and the H scale in src/lib/display.ts.globals.css so font-black renders at 600 across the whole product.Three layers over one component set:
.pro: tighter radii, smaller controls, tabular figures. The counselor and organization consoles..docs: long measure, generous line height. These pages.Colour is expressed as semantic CSS custom properties, so components carry no dark: variants. A .band wrapper flips the whole set to the dark ground, so copy, rules, buttons and chips inside a dark section restyle themselves; a demo that should stay light inside one wraps itself in .light, which restores the base tokens. The site is the paper palette only: the token set still carries a dark variant behinddata-theme="dark", but nothing sets it any more.
Avatars and university crests are generated inline SVG, no image files, no external requests, no broken images. University crests derive their colours and monogram deterministically from the institution's name via an FNV-1a hash.
| Variable | Effect |
|---|---|
CLERK_SECRET_KEY | Clerk server key. Must be set in production. |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Clerk browser key. Publishable by design. |
FLEDGY_DB_PATH | SQLite file location. Defaults to data/fledgy.db. |
RESEND_API_KEY | Enables outbound email. |
FLEDGY_MAIL_FROM | Sender address. Required alongside the API key. |
FLEDGY_BACKUP_DIR | Where nightly backups are written. Defaults to backups/ beside the database. |
FLEDGY_DISABLE_BACKUPS | Set to 1 to skip the nightly backup. Used by CI. |
University logos need no configuration: /api/uni-logo resolves a name to its domain server-side and proxies the icon, so the browser never contacts a logo vendor. Crests remain the fallback. | |
Pasting a Fledgy URL into a chat app produces a card, generated at build time by app/opengraph-image.tsx with ImageResponse. It is drawn from the same palette as the product, using the Noto Sans faces already bundled for the PDF export: Satori needs real font files and cannot use the app's system stack, and it fetches emoji from a CDN, which is why the bird is drawn as SVG primitives rather than written as an emoji. Nothing about the card touches the network.
Shared applications and talent profiles deliberately have no preview image. An unfurl is generated and cached by whichever service the link passes through, usually before a person opens it and outside anything this app can authorise. A card carrying a student's name, school or college list would be copied into every one of them.
The product was Unidash, then Dashly, and is now Fledgy AI. Each rename renamed the variables with it, and every older name is still read as a fallback: FLEDGY_ first, then DASHLY_, then UNIDASH_. This is deliberate: an environment still setting an old name would otherwise boot against an empty database, which presents as total data loss rather than as a misconfiguration.
The same applies to the database file. If fledgy.db does not exist and dashly.db or unidash.db does, it is moved into place on boot, WAL and shared-memory sidecars included, rather than a second empty database being created beside a full one.
Known gaps and limitations are catalogued in Data & accuracy.