The source of israelrocklyn.info, the portfolio of Israel Rocklyn, AI, ML & Cloud Engineer.
It is a PHP site with no framework, no database and no build step. Content lives in three PHP data files, pages are rendered from shared templates, and every number on it comes from the evaluation committed in the project's own repository.
| Home | split-portrait hero; a "trace a request" panel where each hop is a project with its measured result; selected work with live CI status; skills with the projects that prove them; engineering notes; an in-browser shell; services; contact |
Case studies (/work/<slug>) |
the problem, an architecture flow, what was built, lessons learned, known limitations, stack, related notes, live CI status and last push |
Engineering notes (/notes/<slug>) |
short write-ups of real problems from the projects (Little's law in model serving, false drift alarms, honest evaluation, thread spinning, abstention) |
Résumé (/resume) |
generated from the same data, with print styles for a clean PDF |
| Live data | each repository's latest GitHub Actions result and last push, fetched server-side in parallel and cached for 30 minutes (app/lib/github.php) |
| Interaction | command palette (Ctrl/⌘ + K), a shell (help, ls, cat, open, trace, …), project filters, dark and light themes; all progressive enhancement, so every page works without JavaScript |
| Content | app/data/profile.php, app/data/projects.php, app/data/notes.php |
| Design | dark technical layout with an amber accent; Space Grotesk, Inter and JetBrains Mono, self-hosted |
| Hosting | Apache + PHP 8.2 or later (20i Linux hosting) |
- No secrets in the repository. The contact recipient and the form-signing secret live in
contact-config.php, one directory above the web root, which is never served or committed. - Contact form defences: a honeypot field; a signed, time-stamped token (forms submitted within 3
seconds of loading, older than 2 hours, or not issued by this site are refused); 5 messages per hour
per visitor; strict validation; and no visitor input in mail headers except a validated
Reply-To. - The recipient address never appears on any page. Visitors' IP addresses are stored only as hashes, for the rate limit.
- Status messages travel as fixed codes (
?contact=sent), so nobody can craft a link that makes the site display arbitrary text. - Headers:
- a Content-Security-Policy that allows only same-origin scripts, styles and fonts (no inline code)
- HSTS,
X-Frame-Options: DENY,nosniffand a strict referrer policy
- Fonts and images are self-hosted, so pages make no third-party requests from the browser. The GitHub status check runs on the server.
- Internal files are never served:
app/,tests/, the repository metadata and this README all return 404.
/insights is a password-protected dashboard for the site owner. It is not linked anywhere, and it
is sent with noindex and no-store. It shows two things:
- Portfolio visits: page views, daily unique visitors, clicks on links, time on page, top pages,
referrers, devices, browsers and regions. Counting is cookie-free: a visitor is a hash with a
random salt that changes daily, no IP address is written to disk, bots are counted apart, and
browsers that send Do Not Track or Global Privacy Control are not counted (
app/lib/analytics.php). - GitHub traffic: views, unique visitors, clones and unique cloners per repository, plus
referrers and popular content. GitHub keeps only 14 days, so each collection merges them into a
history and weekly and monthly totals build up over time (
app/lib/ghtraffic.php). This needs a fine-grained, read-only token (Administration: read), pasted once in the dashboard's settings.
All data lives in .portfolio-cache/analytics/, above the web root. The password is a bcrypt hash
in settings.json there. Sessions are Secure, HttpOnly and SameSite=Strict, expire after 8 idle
hours, and sign-in is throttled after 5 failures. Every form carries a CSRF token.
php -S localhost:8000 router.php # router.php mirrors the .htaccess rules
php tests/run.php # 75 checks: form logic, data integrity, every page renders and is well-formedNo PHP installed? Use Docker:
docker run --rm -p 8000:8000 -v "$PWD:/site" -w /site php:8.3-cli php -S 0.0.0.0:8000 router.php
To see the contact form locally, put a contact-config.php one directory above the project (copy
contact-config.example.php).
-
SSL first. In My20i, go to Manage Hosting → israelrocklyn.info → SSL/TLS and check that the free certificate is active. The site redirects every request to HTTPS.
-
PHP version. Under Web Tools → Change PHP Version, choose PHP 8.3 or later.
-
Connect the repository. Under Web Files → Git™ Version Control, connect
codeROCK-IZZY/Israelrocklyn, branchmain, and deploy it topublic_html. Each later update is one click on Deploy (or turn on automatic deployment). -
Configure the contact form once. In File Manager, create
contact-config.phpin the home directory, next topublic_html, not inside it:<?php return [ 'recipient' => 'your inbox address', 'secret' => 'a long random string', // e.g. from: php -r "echo bin2hex(random_bytes(32));" 'from' => 'no-reply@israelrocklyn.info', ];
Until this file exists, the form is replaced by a short notice, and nothing breaks.
-
Email deliverability. Run Email → DMARC Wizard and enable Domainkeys (DKIM), so messages sent from
no-reply@israelrocklyn.inforeach the inbox instead of spam.
Change the text in app/data/profile.php or app/data/projects.php. A new project needs:
- an entry in
projects.php - optionally, a 1280×800 WebP screenshot in
assets/img/
The home page, its case-study page and the sitemap pick it up automatically. After changing CSS or
JavaScript, bump ASSET_VERSION in app/bootstrap.php so browsers fetch the new files.
Fonts: Space Grotesk, Inter and JetBrains Mono, under the SIL Open Font License (licences).