TrellisClient Project Portal
What it does for you
One place for clients to see progress and approve work.
A zero-dependency client portal with milestones, versioned deliverables, signed approvals, messages, change requests, and invoices, where each client only ever sees their own work.

Who it's for
Small studios that are tired of chasing approvals over email. Each client sees only their own projects, and every approval records who signed off and when.
Screenshots

In more detail
Trellis is a private portal where a small studio's clients can see how their project is going, review and approve files, and ask for changes without chasing email. The studio gets a dashboard of clients, overdue milestones, items waiting for review, and outstanding invoices. Each client sees only their own organization's projects, and every approval records who signed off and when.
Honest limits
Invoices are records only, with no payment processing. Magic links land in a local outbox folder, not real email. The optional Claude polish for weekly updates was never tested end to end, and neither was a real browser upload. Checks were headless screenshots and API-level tests.
How I built it
This row started on the Codex list. On the last day I asked Claude Code to build everything left, and it took Codex rows from the bottom up, so this became a Claude Code build. It was going to be called Porchlight, but #79 already had that name, so we renamed it Trellis. With builders running in parallel, name clashes were a real coordination problem.
Claude Code wrote it to my spec, layer by layer, with tests after each one: the pure security core first (password hashing, CSRF, rate limiting, upload checks, access rules), then the SQLite layer, then the HTTP layer, then the UI. I set the constraints: zero dependencies, fictional data only, demo login only behind a --demo flag, no em dashes, tiny files. It runs on Node 24 with node:http and the built-in node:sqlite.
The live tests spawn the real server on a spare port, sign in, do a CSRF-protected write, and prove a client cannot fetch another org's project or file. I reviewed the screenshots, and Claude fixed what they showed, like sorting "awaiting review" deliverables to the top of the client page.
What I learned
- Row-level security is easiest to trust when there is exactly one gate. Every route resolves its target up to the parent project and calls one
loadProjectcheck. The cross-org tests passed the first time they ran. - Other orgs get a 404, not a 403, so nobody can probe which ids exist.
node:sqliteis good enough for a real local app: migrations in a transaction,CHECKconstraints for roles and statuses, and WAL mode, with no npm package at all.- Destroying the socket on an oversized upload made
fetchfail instead of getting a clean error. Draining past the cap and answering 413 at the end fixed it. - A strict CSP with no inline styles means progress bars get their width through the CSSOM after render, not a
styleattribute.
What carried forward
This one fed later builds directly. #61 (Auth Starter) imports Trellis's email validation and escaping, and #62 (SaaS Skeleton) imports its security helpers, cookies, rate limiting, and validation, both through a single lib/reuse.js file by relative path. Imported, not copied, so a fix here flows there. Along with #75, it was one of the two most reused projects in the whole sprint. The one-gate idea shows up again in #62, which answers 404 over 403 for non-members.
If I took it further
I would let clients request changes from inside a deliverable and have that open a linked change request automatically. I would also add signed download URLs so file links survive a copy and paste.
#080 Built with Claude Code September 24, 2026 398 automated checks