Admin

The private console that writes Portfolio contracts, IAM access, and deploys—after a live capability check, not a hidden button.

Full-stack engineering · CMS architectureEnd-to-end case study
  • Next.js Server Components
  • Supabase SSR and RLS
  • Typed CMS contracts
  • RBAC and revalidation
Editorial operations are separated from public presentation, so the portfolio can evolve without code edits or duplicated content while authorization remains server-enforced.2025—26 · Full-stack engineering · CMS architecture
01 / Problem

The product tension

If I have to ship a pull request to fix a typo, the public site will rot. If Admin is just hidden buttons, anyone with a cookie becomes an editor. I needed a private operations app that writes the same Work and Writing contracts Portfolio reads, and that re-checks who I am on every privileged call.

02 / Solution

What I built

I built Admin as an IAM-gated Next.js console. Workspaces cover Portfolio CMS, users and access, Vercel deploys for Studio and Admin, and a private career tracker. There is no Studio CMS workspace; Studio owns its own registries.

Authorization. admin.console.enter is the door. admin.full_access mutates. admin.viewer reads unpublished Portfolio content and deployments. Proxy admission is not enough: each elevated route reauthorizes, then may create a service-role client. Role names in the JWT are not the check.

Contracts. Forms validate with @jayantgoyal/portfolio-contracts—the same columns, JSON case-study shape, and assets the public loader selects. Saves revalidate Portfolio. Identity name stays read-only from shared config.

Other domains. Career reads a private schema after admin.career.read. Deployment actions use a server-only Vercel token. Assignment and revocation of Admin access go through transactional IAM functions that write an audit event.

03 / Architecture

How the system fits together

Admin is a client of Portfolio, IAM, and providers. It does not become the owner of public copy or of Auth credentials.

Drawing architecture diagram…
04 / Decisions

The calls that shaped the build

  1. 01

    I authorize the mutation, not the menu

    Hiding a nav item is not security. Viewer and full-access see different workspaces, but writes still go through capability-checked server routes before a service-role client exists.

  2. 02

    I kept one document shape for case studies

    Admin cannot save a study the public renderer cannot parse. Decisions, architecture, and publication flags are fields, not a blob of HTML.

  3. 03

    I left Studio out of this console

    A reserved Studio nav with no workspace is honest. Inventing Admin screens for tools I already register in code would duplicate ownership.

05 / Security

Trust boundaries

The app is private. MFA step-up applies when a factor exists. Service-role and Vercel tokens never ship to the browser. Public Portfolio queries never receive unpublished rows or privileged credentials. Career data is not a public grant.

06 / Tradeoffs

What I chose—and accepted

A second application to deploy, plus IAM ceremony on every write. Scripts against the database are faster for me and invisible to an audit. I still want the form, the contract, and the revalidation on the path a future editor would use.

07 / Outcome

Why the work matters

I can publish or unpublish a case study without mixing CMS JSON into the Next app. The public site stays free of service-role. Access changes leave an IAM audit trail. Admin sits on the home grid as the operations half of the publishing system, not as a fifth product family.

08 / Next

What I would improve next

Preview and revision history before a Work save goes live. Until then, publication remains an explicit flag, not an autosave side effect.