Command: cat projects/this-site.md
This site
A portfolio that works like a terminal session: every page answers a command, and the next commands are links, so anyone can navigate by clicking. People who prefer typing get a real terminal and a command palette, and all the content reads without JavaScript. The site itself is the evidence: its HTML, headers and commands can be checked in the browser.
- Role
- Concept, architecture, development and content
- Period
- Sep 2026 – present
- What I did
Me: Concept, architecture, security and privacy rules, content and every design decision, from the virtual filesystem to the color palette.
Diagram
Typed content is the single source. It produces the static HTML pages, readable without JavaScript, the virtual file system with the command output, as JSON, and the command palette's actions. The terminal runs its own shell on that data and draws the output as semantic HTML. The contact form, one of the pages, posts to the only server function, which sends the e-mail through Resend.
Context and problem
My previous portfolio only showed its content with JavaScript and sent no security headers. For someone who wants to work in security, their own site is the first thing people will test.
This site starts from another premise: it is my working environment, a terminal window, and every page is the output of a command. Nobody needs to type anything: the next commands appear at the end of every page, as links.
Architecture
- One content source. Typed content generates the pages, the virtual filesystem, the commands’ output and the palette’s actions. A test checks that every file has a page and a command.
- A real shell underneath. Commands with GNU-style options, error messages with the real text and exit codes of zsh and coreutils, a manual generated from each command’s definition, and contract tests that run every example.
- A code rain made of real data. This build’s hash, the command names and the file paths. It stays outside the reading window, has a pause button and starts off for people who ask for less motion.
- Contact with no third parties in the browser. A form that works without JavaScript, with validation and spam protection on the server.
What I would do differently
I would write the privacy guard before reading any repository, not right after. It was the first control I wanted and the last one I thought of building.
Constraints
- All content reachable without typing commands and without JavaScript.
- One content source for the pages, the virtual filesystem and the commands.
- No client, company or private repository name in a tracked file without authorization.
- WCAG 2.2 AA, with keyboard and screen reader.
- Deploys only with the whole CI green.
Decisions
Static Astro instead of an application framework
- Context
- The content is static and must read without JavaScript; the terminal, the command palette and the rain are layers on top of it.
- Choice
- Astro generating static HTML, with a single server function (the contact form).
- Gains
- No framework in the browser to read the content.
- Hash-based CSP on every page.
- Costs
- Interactive features need separate islands and extra care with the CSP.
Terminal commands as data, not loose text
- Context
- I wanted the terminal metaphor to be real: commands with options, errors and real exit codes, not a typing animation.
- Choice
- A shell core of my own (POSIX-style tokenizer, GNU-style option parser, zsh and coreutils messages) that produces structured output, rendered as semantic HTML.
- Gains
- Accessible and indexable; every command has a manual and a contract test.
- Costs
- More code to maintain than a decorative terminal.
A privacy guard with the blocklist outside the repository
- Context
- The content came from reading private repositories; real names must not leak in a commit.
- Choice
- A pre-commit hook and a CI job compare tracked files with a list kept outside the repository (locally and as a CI secret). The log points only to the term's index, never the term.
- Gains
- The repository can be public without exposing the names the guard protects.
- Costs
- It depends on keeping the list up to date.
Stack and why
- Astro 7 and Preact
- Static HTML by default and small islands for what is interactive.
- Strict TypeScript
- Contracts between content, the virtual filesystem and the commands.
- Vitest and Playwright
- Unit tests for the shell core, the commands, the session, the contact form and the rain; in the browser, every page with and without JavaScript, with accessibility checks.
- Resend
- Sending the contact form, validated on the server.
- Vercel
- Static hosting with one function; deploys only through CI.
Results
All the content, case studies, CV and security journal included, reads and navigates without JavaScript.
public linkThe site itself, with JavaScript turned off(Sep 2026)Every text color of both themes is checked against every window background by a test that reads the colors from the code itself, and every pair passes WCAG AA contrast.
public linkHow this site was built(Sep 2026)
Security angle
Attack surface
- The contact form (the only server function).
- Static JSON files with the commands' data.
Controls in place
- Hash-based CSP, frame-ancestors 'none' and security headers.
- No HTML built from strings, with a lint rule that rejects innerHTML and similar APIs.
- Server-side validation, honeypot, time token, rate limit and a daily budget on the contact form.
- Secrets only in the host's environment variables; secret scanning in CI.
What I would test today
- XSS through URL parameters and the form.
- Abusing the form to exhaust the daily e-mail quota.
- Clickjacking and CSP bypasses.
Evidence
- public linkHow this site was built