Objective and scope
This site needs to do two jobs: publish a useful professional record and let me maintain it through a CMS. Draft pages, deleted records, private documents, and unfinished qualifications must not appear as public evidence. The repository is private, so the case study includes selected implementation excerpts visitors can read here.
Contribution and decisions
My role is to set the content requirements, review the career claims, and maintain the portfolio. This iteration includes AI-assisted implementation and review. The design keeps public reading, authenticated editing, and credential-document release as separate decisions. Server-rendered pages read through the CMS access rules instead of exposing the content collections directly to anonymous API clients.
Implementation evidence
Public reading
- Visitor
- Next.js public routes
- published Payload records
- PostgreSQL
Content editing
- Authenticated administrator
- Payload CMS
- drafts / published records
Media
- Published pages
- media references
- Vercel Blob
cms/access.ts · collection read policy
const publishedAndNotDeleted: Where = {
and: [
{ _status: { equals: "published" } },
{ deletedAt: { exists: false } },
],
};
export const publishedOrAuthenticated: Access = ({ req }) => {
if (req.user) return true;
if (req.payloadAPI && req.payloadAPI !== "local") return false;
return publishedAndNotDeleted;
};Environment and structure
Public pages use the Next.js App Router and server-side Payload queries. PostgreSQL stores the records; Payload provides the authenticated editing interface. Media is stored in Vercel Blob. The diagram shows these application relationships, not a deployed network topology. A media reference is separate from permission to publish a credential download.
Defensive controls
The excerpt has three branches: an authenticated request may read the collection; an anonymous remote API request is denied; an anonymous local query receives a condition requiring both published status and no deletion timestamp. Public page queries also use a publication condition. Draft preview is a separate authenticated path. Credential downloads have their own publication checks; this one function does not enforce every boundary in the application.
Lessons learned
An accurate page is only part of the job. The CMS state, access rules, linked documents, and deployment checks also need to agree. A status label in the interface cannot substitute for a rule at the data boundary.
Validation record
14 October 2026 (UTC) — revision 5391484b83c849d0e9be3d6c75e15b9487655058: the complete verification run recorded 609 passing unit tests and 4 skipped tests across 57 files. Its full browser run recorded 56 passes and one failure caused by a stale roadmap-title expectation.
14 October 2026 (UTC) — revision 055b99af1e7300fda0a54bfdf7a7937293c83f29: after correcting that metadata expectation, GitHub Actions run 38499116464 passed the required targeted lanes, including 5 public browser tests. This was a targeted rerun, not a second complete browser suite.
These are historical checks of those revisions. CI logs are in the private repository; these results are a published summary, not a public reproduction package or an independent assessment of the live service.
Implementation and findings
The site supports CMS editing, project and credential detail pages, authenticated previews, and public document links that depend on explicit release fields. The publication condition and request branching shown here are implemented source, not a proposed control. The access-policy unit tests exercise returned conditions with synthetic request objects; browser tests cover separate application workflows.
Limits and next steps
The full repository is private. Visitors can inspect the running site, the architecture diagram, and the selected source excerpts here. The historical checks do not establish that every deployed path is defect-free. This is personal application work, not professional penetration testing, incident response, or a completed security lab. Further work includes clearer public examples of credential-release behavior and keeping this record aligned with subsequent releases.
Authorized scope
The Security page describes the live service’s testing policy. This case study does not expand that authorization.
Tools and technologies
Next.js · TypeScript · Payload CMS · PostgreSQL · Vitest · Playwright