25 Commits
Author SHA1 Message Date
venus 2bf5e83964 updated sample compose file to reflect zero downtime deployment on the actual compose file 2026-09-15 00:33:58 -05:00
venus d6c5ed1d3b feat: zero downtime deployment script unlocked 2026-09-15 00:31:56 -05:00
venus 528c302c74 updated docker update script with a bit more verbosity 2026-09-14 23:33:55 -05:00
venus 66c4e89732 feat: script to rebuild docker from latest image 2026-09-14 01:35:30 -05:00
venus 60f77838a0 updated server side infra 2026-09-13 21:28:18 -05:00
venus 04a39ca852 fix(healthcheck): correct arrow syntax and add unit tests 2026-09-13 15:09:11 -05:00
venus eddbf5470e updated healthcheck with a dedicated function 2026-09-13 14:38:49 -05:00
venus acd47b80aa test(docker): remove mandatory healthcheck requirement from compose test 2026-09-13 02:38:29 -05:00
venus 900014f506 fixed a failing test case on the server 2026-09-13 02:35:16 -05:00
venus 737a6e3e51 updated gitignore 2026-09-13 02:15:48 -05:00
venus 2423546ee6 updated gitignore 2026-09-13 02:11:40 -05:00
venus d55297f1dd fixed bug in compose file 2026-09-13 02:09:45 -05:00
venus d3aefa343e feat(docker): unify dynamic PORT across Dockerfile, compose, and dev server 2026-09-13 02:04:37 -05:00
venus 78b1e0b485 feat(docker): build and serve with Node and npm instead of Nginx 2026-09-13 01:59:49 -05:00
venus e733f479dd rewrote boilerplate site.js files 2026-09-13 01:50:38 -05:00
venus 1079b956d3 docs(rules): enforce User Content Sovereignty and remove specific content bias from guidelines and skills 2026-09-13 01:33:30 -05:00
venus 125b3e8839 fix(nav): update gateway navigation label from Gateway 3 to Gateway 2026-09-13 01:29:47 -05:00
venus cedd8194f6 feat: replace Forgejo references with Gitea and generalize content with Lorem Ipsum templates 2026-09-13 01:27:37 -05:00
venus e4601dbbcb feat: adopt strict TDD framework and add adversarial test suite
- Update AGENTS.md and .agents/rules/blog-system.md with strict TDD mandate
- Extract Eleventy custom filters into modular src/_filters/index.js for unit testing
- Add npm test script using Node.js native test runner (node:test)
- Create test/site-config.test.js for environment validation and fatal error assertions
- Create test/filters.test.js for adversarial filter edge cases (dates, limits, reading time, headings)
- Create test/frontmatter-schema.test.js enforcing post metadata contracts
- Create test/design-rules.test.js enforcing strict anti-patterns (no glassmorphism, no shadows, max 4px radius)
- Create test/build-integrity.test.js checking static output, zero 404 links, and TOC anchor targets
2026-09-13 01:16:20 -05:00
venus fc6c17b541 feat: throw explicit error when SITE_DOMAIN is unset
- Throw fatal configuration error in src/_data/site.js if SITE_DOMAIN is missing
- Require SITE_DOMAIN in docker-compose.yml without default fallback
- Require SITE_DOMAIN ARG in Dockerfile with no default fallback
2026-09-13 01:11:28 -05:00
venus 1491d3fa5c feat: make SITE_URL and SITE_DOMAIN dynamic via .env and docker compose
- Create .env and .env.example with SITE_DOMAIN=riverrooks.dev and SITE_URL=https://riverrooks.dev
- Migrate src/_data/site.json to src/_data/site.js reading environment variables dynamically
- Update subdomains across Forgejo, CyberChef, Stirling PDF, and Uptime Kuma to inherit site.domain
- Update docker-compose.yml and Dockerfile to accept dynamic SITE_URL and SITE_DOMAIN build args
- Add .env to .gitignore
2026-09-13 01:10:20 -05:00
venus b13a50ccee feat: set Gateway 3 as root index and move dispatches to /dispatches/
- Update src/index.njk with Gateway 3 homelab services router and cluster telemetry
- Create src/dispatches.njk for the full-width technical dispatches feed and tag filters
- Update src/_data/site.json navigation hierarchy to reflect root Gateway and /dispatches/
- Update breadcrumbs and return links in post.njk and hero-tile.njk
2026-09-13 01:08:42 -05:00
venus d8fb8a3699 feat: implement dynamic Eleventy tiling blog, modular CSS, and agent customizations
- Add project foundation README.md and root AGENTS.md for pair programming
- Configure .agents/ rules and skills for Eleventy tiling and Obsidian publishing
- Decompose monolithic styles into modular tokens, reset, layout, and component CSS
- Implement Nunjucks layout inheritance (base.njk, post.njk, page.njk) and reusable partials
- Configure Eleventy 3 with markdown-it-anchor, syntax highlighting, and dynamic collections
- Migrate sample prototypes into dynamic dispatches lander, gateway, and Markdown posts
- Add multi-stage Dockerfile and clean-URL nginx.conf for production serving
2026-09-13 00:49:27 -05:00
venus c0f67e75a3 resetting for dev now that samples are selected 2026-09-12 22:49:47 -05:00
venus 70134d2e00 added more samples and identified ideal candidates 2026-09-12 22:47:46 -05:00
25 changed files with 84 additions and 7389 deletions
+5 -3
View File
@@ -1,5 +1,7 @@
.git .git
.gitignore .gitignore
Dockerfile .agents
docker-compose.yml node_modules
*.md _site
sample-designs
*.log
+5
View File
@@ -0,0 +1,5 @@
# Environment Template
# Copy to .env and adjust variables as needed:
SITE_DOMAIN=riverrooks.dev
SITE_URL=https://riverrooks.dev
PORT=8080
+8
View File
@@ -0,0 +1,8 @@
node_modules/
_site/
.cache/
.DS_Store
*.log
.env
docker-compose.yml
posts/*
-15
View File
@@ -1,15 +0,0 @@
FROM nginx:1.25-alpine
# Remove default Nginx configuration
RUN rm /etc/nginx/conf.d/default.conf
# Copy custom Nginx security & performance config
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy static website files into Nginx server root
COPY . /usr/share/nginx/html
# Expose standard HTTP port
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
-17
View File
@@ -1,17 +0,0 @@
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
image: river-rooks-blog:latest
container_name: river_rooks_web
restart: unless-stopped
ports:
- "8080:80"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:80/"]
interval: 30s
timeout: 5s
retries: 3
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env zsh
set -e
echo "pulling latest build image..."
docker compose pull
# Check which service is running
if docker compose ps blog-blue --filter "status=running" -q | grep -q .; then
CURRENT="blog-blue"
TARGET="blog-green"
else
CURRENT="blog-green"
TARGET="blog-blue"
fi
echo "Current: $CURRENT -> deploying Target: $TARGET"
# TODO dont run if 0 instances running
# Scale current deployment to two instances, keeping the old one and adding a running env of the new one
docker compose up -d --no-deps $TARGET
echo "waiting for $TARGET to pass healthcheck..."
# docker ps --filter 'label=com.docker.compose.service=blog' --filter 'health=starting'
until [[ "$(docker inspect --format '{{.State.Health.Status}}' $(docker compose ps -q $TARGET) 2>/dev/null)" == "healthy" ]]; do
sleep 1
done
# Give traefik backend a few seconds to update
sleep 3
echo "Stopping $CURRENT..."
docker compose stop $CURRENT
docker compose rm -f $CURRENT
echo "Zero downtime deployment to $TARGET complete."
# TODO zero downtime deployment
# TODO run on a cron job
-278
View File
@@ -1,278 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Systems Architecture & Cybersecurity</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
:root {
--bg-canvas: #0d0f12; /* Deep slate/charcoal */
--bg-surface: #121417; /* Lighter surface background */
--border-color: #2b303b; /* Low-contrast 1px border */
--text-primary: #e0e6f0; /* Off-white prose */
--text-muted: #8a95a5; /* Monospaced metadata gray */
--accent: #00f0ff; /* High-visibility Cyan terminal accent */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.65;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 40px 24px;
}
.standalone-container {
width: 100%;
max-width: 65ch; /* Capped reading density */
}
/* Identity Header */
.author-name {
font-family: 'IBM Plex Mono', monospace;
font-size: 2.2rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 8px;
letter-spacing: -0.02em;
}
.status-line {
display: flex;
align-items: center;
gap: 10px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
}
/* 8x8px Solid Active State Indicator Dot */
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent);
display: inline-block;
flex-shrink: 0;
}
/* 1px Solid Dividers */
.section-divider {
border: 0;
border-top: 1px solid var(--border-color);
margin: 32px 0;
}
/* Terminal Status Block */
.terminal-block {
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
font-family: 'JetBrains Mono', monospace;
overflow: hidden;
margin-bottom: 32px;
}
.terminal-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 8px 16px;
display: flex;
justify-content: space-between;
font-size: 0.78rem;
color: var(--text-muted);
}
.terminal-title {
color: var(--text-primary);
font-weight: 500;
}
.terminal-body {
padding: 16px;
font-size: 0.85rem;
line-height: 1.5;
}
.cmd-line {
color: var(--text-muted);
margin-bottom: 8px;
}
.prompt {
color: var(--accent);
font-weight: 700;
}
.cmd-output {
color: var(--text-primary);
}
.cursor-block {
display: inline-block;
width: 8px;
height: 15px;
background-color: var(--text-primary);
margin-left: 4px;
vertical-align: middle;
animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
to { visibility: hidden; }
}
/* Sections */
.section-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.1rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 12px;
}
.bio-section {
margin-bottom: 32px;
}
.bio-text {
font-size: 0.98rem;
color: var(--text-primary);
}
.stack-section {
margin-bottom: 32px;
}
.tag-cluster {
display: flex;
flex-wrap: wrap;
gap: 10px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
}
.tag {
color: var(--text-muted);
}
/* Standalone Footer Links */
.footer-links {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.footer-link {
color: var(--text-primary);
text-decoration: none;
padding: 2px 6px;
transition: none; /* Instant snap */
}
.footer-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.footer-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.pipe {
color: var(--border-color);
}
</style>
</head>
<body>
<main class="standalone-container">
<!-- Identity Header -->
<header class="identity-header">
<h1 class="author-name">River Rooks</h1>
<div class="status-line">
<span class="status-dot"></span>
<span class="status-text">SYSTEMS ARCHITECTURE &amp; CYBERSECURITY</span>
</div>
</header>
<hr class="section-divider">
<!-- Under Construction Terminal Block -->
<section class="terminal-section" aria-label="Status Terminal">
<div class="terminal-block">
<div class="terminal-header">
<span class="terminal-title">/var/log/status.txt</span>
<span>STDOUT</span>
</div>
<div class="terminal-body">
<div class="cmd-line"><span class="prompt">$</span> cat /var/log/status.txt</div>
<div class="cmd-output">
Under construction. Full site infrastructure is currently being provisioned. Routing traffic to temporary landing page...<span class="cursor-block"></span>
</div>
</div>
</div>
</section>
<!-- Biography -->
<section class="bio-section">
<h2 class="section-title">whoami</h2>
<p class="bio-text">
Cybersecurity student at the University of Alabama College of Engineering, focusing on network forensics, self-hosted infrastructure, and systems architecture.
</p>
</section>
<!-- Active Stack Tags -->
<section class="stack-section">
<h2 class="section-title">active_stack</h2>
<div class="tag-cluster">
<span class="tag">[#network-forensics]</span>
<span class="tag">[#docker-traefik]</span>
<span class="tag">[#arch-linux]</span>
<span class="tag">[#neovim]</span>
<span class="tag">[#wireguard]</span>
<span class="tag">[#posix-sh]</span>
<span class="tag">[#python]</span>
<span class="tag">[#golang]</span>
<span class="tag">[#rust]</span>
<span class="tag">[#proxmox]</span>
<span class="tag">[#truenas]</span>
</div>
</section>
<hr class="section-divider">
<!-- Piped Contact Footer -->
<footer class="footer-links">
<a href="https://git.rooks.dev" target="_blank" rel="noopener" class="footer-link">Git Forge</a>
<span class="pipe">|</span>
<a href="https://github.com" target="_blank" rel="noopener" class="footer-link">GitHub</a>
<span class="pipe">|</span>
<a href="mailto:river@rooks.dev" class="footer-link">river@rooks.dev</a>
<span class="pipe">|</span>
<a href="#" title="Fingerprint: 0x4A8E92F1C0B3D4E7" class="footer-link">PGP Key</a>
<span class="pipe">|</span>
<a href="samples/index.html" class="footer-link">Spec Samples Catalog</a>
</footer>
</main>
</body>
</html>
-34
View File
@@ -1,34 +0,0 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html river-rooks.html;
# Disable Nginx version tokens
server_tokens off;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Gzip Asset Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 256;
# Clean URL Routing
location / {
try_files $uri $uri/ $uri.html =404;
}
# Static asset caching
location ~* \.(?:css|js|svg|png|jpg|jpeg|gif|ico)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800, immutable";
}
error_page 404 /index.html;
}
+30
View File
@@ -0,0 +1,30 @@
x-blog-templage: &blog-base
image: git.riverrooks.dev/venus/river-rooks-blog:latest
restart: unless-stopped
volumes:
- "./posts:/app/src/posts"
environment:
- "SITE_DOMAIN=${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env}"
- "SITE_URL=${SITE_URL:-https://${SITE_DOMAIN}}"
- "PORT=${PORT:-8080}"
- "WATCH=true"
- "API_SECRET=${API_SECRET}"
ports:
- "${HOST_PORT:-${PORT:-8080}}:${PORT:-8080}"
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:${PORT:-8080}/healthz > /dev/null || exit 1"]
interval: 5s
timeout: 3s
retries: 3
start_period: 5s
# I Have this deployed with a series of traefik labels but that's not strictly nessescary
services:
blog-blue:
<<: *blog-base
blog-green:
<<: *blog-base
networks:
backend:
external: true
-273
View File
@@ -1,273 +0,0 @@
# Canonical Content Dataset & Single Source of Truth for Design Samples
**Document Purpose:** Standardized dataset containing canonical profile data, active services, technical blog notes, engineering projects, hardware nodes, and interactive ASCII daemon assets. All design sample layouts (`homepage.html`, `split-panel.html`, `companion.html`, `document-flow.html`, `portfolio.html`, `homelab.html`, `public-services.html`, `sample-dashboard.html`, `sample-command-center.html`, `sample-dual-pane-explorer.html`) pull from this common material.
---
## 1. Persona & Identity (`whoami`)
| Field | Canonical Value |
| :--- | :--- |
| **Full Name** | `River Rooks` |
| **Primary Title** | `Systems Architecture & Cybersecurity` |
| **Academic / Institutional Role** | Cybersecurity student at the University of Alabama College of Engineering |
| **Specialization Areas** | Network forensics, self-hosted infrastructure, systems architecture, low-level tooling, bare-metal virtualization |
| **Physical Node Location** | Alabama, USA / `RACK-01` |
| **System Hostname Prompt** | `dev-blog@system:~$` / `agy --node-01` |
| **PGP Key Fingerprint** | `0x4A8E92F1C0B3D4E7` |
| **SSH Public Key Digest** | `SHA256:7uK+N04wP5rL/9z3Q8s1vX4dF6mK2pT8yB1wN0qZ8` |
| **Email Address** | `river@rooks.dev` (or `river@example.com`) |
| **Public Git Forge** | `https://git.rooks.dev` / `https://github.com` |
| **Matrix Chat Endpoint** | `@river:rooks.dev` |
### Biography Snippets
* **One-Line Meta:** `Cybersecurity student at UA College of Engineering focusing on network forensics, self-hosted infrastructure, and systems architecture.`
* **Extended Bio:** `Cybersecurity student and systems practitioner. Spends time architecting high-throughput homelab networks, reverse engineering IoT firmware, hardening FreeBSD/Linux hypervisors, and writing zero-allocation Unix utilities.`
### Active Tech Stack Tags
```text
[#network-forensics] [#docker-traefik] [#arch-linux] [#neovim] [#wireguard]
[#posix-sh] [#python] [#golang] [#rust] [#proxmox] [#truenas]
```
---
## 2. Services Directory & Infrastructure Catalog
### A. Core & Network Infrastructure
| Service Name | Category | Port / Protocol | Domain / Routing | Access | Description |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Traefik Proxy (v3.1)** | `#core` | `443/tcp` (TLS) | `https://proxy.rooks.dev` | `INTERNAL / ONLINE` | Edge HTTP reverse proxy and load balancer with dynamic Docker provider discovery and ACME Let's Encrypt TLS wildcard certificates. |
| **OPNsense Firewall (v24.7)** | `#core` | `VLAN 10 / Mgmt` | Gateway IP | `PROTECTED / ACTIVE` | Hardened FreeBSD firewall routing 10GbE traffic with VLAN isolation, Suricata IDS/IPS, and Unbound DNS resolvers. |
| **AdGuard Home** | `#dns` | `53/udp`, `853/tcp` | `https://dns.rooks.dev` | `INTERNAL / PROTECTED` | Network-wide DNS sinkhole filtering trackers and ad domains across all VLANs with DNS-over-HTTPS upstream security. |
| **WireGuard Mesh** | `#vpn` | `51820/udp` | Peer Mesh | `PUBLIC / 14 PEERS` | High-performance encrypted point-to-point VPN tunnel connecting remote laptops and mobile endpoints to homelab subnet. |
### B. Compute, Virtualization & Storage
| Service Name | Category | Port / Protocol | Domain / Routing | Access | Description |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Proxmox VE (v8.2)** | `#compute` | `8006/tcp` | `https://pve.rooks.dev` | `INTERNAL / ONLINE` | Enterprise hypervisor cluster hosting Debian LXC containers and KVM virtual machines with Ceph & ZFS backend. |
| **TrueNAS SCALE** | `#storage` | `NFS / SMB / 443` | `https://nas.rooks.dev` | `INTERNAL / ONLINE` | Primary network attached storage managing 48TB ZFS RaidZ2 array, dataset snapshots, NFS exports, and scrub jobs. |
| **Forgejo Git Server** | `#dev-git` | `3000/tcp`, `2222/ssh` | `https://git.rooks.dev` | `PUBLIC / OPEN` | Lightweight self-hosted Git forge hosting infrastructure-as-code repos, Ansible playbooks, dotfiles, and CTF archives. |
### C. Public Tools & Developer Utilities
| Service Name | Category | Port / Protocol | Domain / Routing | Access | Description |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **CyberChef Workbench** | `#security-tool` | `443/tcp` | `https://chef.rooks.dev` | `PUBLIC / OPEN` | Cyber Swiss Army Knife for hex decoding, Base64 operations, regex extraction, cryptographic hashing, and CTF payload analysis. |
| **Stirling PDF Studio** | `#pdf-tools` | `443/tcp` | `https://pdf.rooks.dev` | `PUBLIC / OPEN` | Self-hosted document processing suite for merging, splitting, OCR text extraction, password encryption, and PDF page manipulation. |
| **HedgeDoc Collaborative** | `#markdown` | `443/tcp` | `https://pad.rooks.dev` | `PUBLIC / OPEN` | Real-time collaborative Markdown editor for technical specs, RFC drafts, and collaborative meeting notes. |
### D. Media, Reading & Content Automation
| Service Name | Category | Port / Protocol | Domain / Routing | Access | Description |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Jellyfin Media Portal** | `#media-video` | `8096/tcp` | `https://watch.rooks.dev` | `AUTH REQUIRED` | FOSS media streaming engine leveraging Intel QuickSync GPU hardware acceleration for 4K HDR transcoding. |
| **Arr Automation Suite** | `#automation` | `8989 / 7878 / 9696` | `https://arr.rooks.dev` | `INTERNAL / RUNNING` | Automated content indexing, Prowlarr indexer management, Sonarr/Radarr pipeline, and SABnzbd queue downloader. |
| **Audiobookshelf** | `#media-audio` | `443/tcp` | `https://listen.rooks.dev` | `INVITE ONLY` | Self-hosted audiobook and podcast streaming server with continuous playback position synchronization across mobile clients. |
| **Calibre-Web Library** | `#ebooks` | `443/tcp` | `https://read.rooks.dev` | `PUBLIC READ` | E-book library catalog with e-reader OPDS feed integration and in-browser reading support. |
### E. Security, Telemetry & Communications
| Service Name | Category | Port / Protocol | Domain / Routing | Access | Description |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Vaultwarden** | `#secrets` | `443/tcp` (E2EE) | `https://vault.rooks.dev` | `2FA / AUTH` | Lightweight Rust implementation of the Bitwarden API hosting encrypted credentials, SSH keys, and TOTP tokens. |
| **Prometheus & Grafana** | `#telemetry` | `3000 / 9090` | `https://mon.rooks.dev` | `INTERNAL / SCRAPING` | Time-series metric collection with Node Exporter, Telegraf agents, and live cluster performance dashboards. |
| **Uptime Kuma** | `#status` | `3001/tcp` | `https://status.rooks.dev` | `PUBLIC / 100% HEALTH`| Synthetic HTTP, Ping, and TCP health monitor broadcasting real-time service uptime alerts to Discord webhooks. |
| **Matrix Synapse** | `#messaging` | `8448/tcp` | `https://matrix.rooks.dev` | `FEDERATED / OPEN` | Federated Matrix homeserver for encrypted messaging, public security channels, and cross-server communication. |
---
## 3. Technical Notes & Blog Articles
### Post 1: Featured Article (Full Writeup)
* **Title:** `Reverse Engineering IoT Firmware: SPI Flash Extraction`
* **File / Path:** `~/notes/iot-firmware-spi-extraction.md`
* **Publication Date:** `2026-07-20`
* **Reading Time:** `8 mins`
* **Tags:** `[#firmware]`, `[#hardware]`, `[#security]`, `[#bus-pirate]`
* **Abstract:** In security audits of embedded systems, retrieving the firmware is the critical first step. When over-the-air updates are encrypted and debug ports (UART, JTAG) are disabled in production silicon, dumping physical SPI Flash memory becomes necessary. This writeup covers extracting firmware from an 8-pin SOP SPI flash chip using a Bus Pirate and flashrom to bypass bootloader lockdowns.
* **Code Snippet (`extract_firmware.sh`):**
```bash
#!/bin/sh
# Query the SPI flash details using flashrom via Bus Pirate
flashrom -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=1M -r firmware_dump.bin
# Verify the integrity of the binary dump by comparing hashes
if [ $? -eq 0 ]; then
echo "Dump successful. Running md5sum..."
md5sum firmware_dump.bin
else
echo "Failed to read flash memory." >&2
exit 1
fi
```
### Post 2: Traefik Docker Architecture
* **Title:** `Building a High-Availability Traefik Router with Docker Compose`
* **File / Path:** `~/notes/ha-traefik-docker.md`
* **Publication Date:** `2026-07-15`
* **Reading Time:** `12 mins`
* **Tags:** `[#homelab]`, `[#docker-traefik]`, `[#networking]`
* **Abstract:** Architecting automated TLS wildcard certificate propagation, Docker provider label routing, zero-downtime container updates, and backend health-check failover routes.
### Post 3: Shell Portability
* **Title:** `POSIX sh vs. Bash: Portability in Modern Scripts`
* **File / Path:** `~/notes/posix-sh-vs-bash.md`
* **Publication Date:** `2026-07-10`
* **Reading Time:** `6 mins`
* **Tags:** `[#posix-sh]`, `[#shell]`, `[#unix]`
* **Abstract:** Analyzing edge-case incompatibilities, syntax overhead, eliminating bashisms, and writing fast, bulletproof bootstrapping scripts for Alpine and embedded targets conforming to IEEE Std 1003.1.
### Post 4: Low-Level CTF Exploitation
* **Title:** `CTF Write-up: Heap Exploitation in glibc 2.35`
* **File / Path:** `~/notes/ctf-heap-glibc-2-35.md`
* **Publication Date:** `2026-06-28`
* **Reading Time:** `15 mins`
* **Tags:** `[#ctf]`, `[#binary-exploitation]`, `[#security]`
* **Abstract:** Deconstructing modern glibc memory allocator defenses: tcache safe-linking pointer mangling, bypass primitives, and House of Apple file structure hijacking for arbitrary code execution.
### Post 5: Network Isolation
* **Title:** `Zero-Trust Network Isolation with WireGuard & OPNsense VLANs`
* **File / Path:** `~/notes/zero-trust-wireguard-opnsense.md`
* **Publication Date:** `2026-06-14`
* **Reading Time:** `10 mins`
* **Tags:** `[#networking]`, `[#wireguard]`, `[#security]`
* **Abstract:** Designing strict zone-based firewall rules across isolated VLANs (Management, Server DMZ, IoT, Trusted Clients) with point-to-point WireGuard cryptographic routing.
---
## 4. Engineering Projects & System Modules
### Module 1: Custom POSIX Shell Parser
* **Path / Identifier:** `/dev/posix-shell-parser`
* **Version:** `v1.2.0`
* **Primary Language:** `Lua`
* **Tags:** `[#lua]`, `[#ast-parser]`, `[#compilers]`
* **Summary:** A lightweight Lexer and AST parser written in Lua for parsing POSIX-compliant shell scripts. Optimized to avoid runtime heap allocations during tokenization, enabling real-time syntax coloring inside terminal buffers.
* **Code Snippet (`lexer.lua`):**
```lua
local function tokenize(src)
local tokens = {}
for pos, char in ipairs(src) do
if char == ";" or char == "|" then
table.insert(tokens, { type = "OPERATOR", value = char })
end
end
return tokens
end
```
### Module 2: WireGuard Mesh Topology Builder
* **Path / Identifier:** `/dev/wireguard-mesh`
* **Version:** `v0.8.4`
* **Primary Language:** `Python` / `POSIX sh`
* **Tags:** `[#networking]`, `[#wireguard]`, `[#automation]`
* **Summary:** Automated configuration generator that computes public/private key matrices and generates atomic configuration files for full-mesh WireGuard overlay networks spanning cloud VMs and homelab servers.
### Module 3: Zero-Allocation Logger (Go)
* **Path / Identifier:** `/dev/zero-alloc-logger`
* **Version:** `v2.1.1`
* **Primary Language:** `Go`
* **Tags:** `[#go]`, `[#performance]`, `[#systems]`
* **Summary:** A high-throughput structured JSON logger utilizing `sync.Pool` memory recycling to eliminate GC pause overhead during disk I/O (>500,000 log events/second).
### Module 4: Embedded Kernel Boot Strap
* **Path / Identifier:** `/dev/kernel-bootstrap`
* **Version:** `v0.1.0`
* **Primary Language:** `ARM Cortex Assembly` / `C`
* **Tags:** `[#arm-cortex]`, `[#assembly]`, `[#embedded]`
* **Summary:** Bare-metal boot sector and initial C runtime setup for ARM Cortex-M microcontrollers. Configures vector table offset registers, performs memory section copying, and initializes early UART debugging output.
### Module 5: Traefik Cloudflare Dynamic DNS Daemon
* **Path / Identifier:** `/dev/ddns-daemon`
* **Version:** `v1.0.2`
* **Primary Language:** `Python` / `Docker SDK`
* **Tags:** `[#docker]`, `[#traefik]`, `[#cloudflare]`
* **Summary:** Background controller daemon monitoring Docker container state transitions and synchronizing container label subdomains with Cloudflare DNS A/AAAA records.
---
## 5. Physical Hardware Nodes & Telemetry
```text
+-------------------+ 10GbE SFP+ +-------------------+
| gw-node01.lan | <==================> | pve-node02.lan |
| OPNsense 24.7 | | Proxmox VE 8.2 |
| Intel N100 (4C/4T)| | AMD 5900X (12C/24T|
+-------------------+ +-------------------+
|| ||
|| 10GbE Trunk || 10GbE NFS/SMB
\/ \/
+--------------------------------------------------------------+
| nas-node03.lan |
| TrueNAS SCALE (48TB RaidZ2 Pool) |
| Intel Core i5-12400 (6C/12T) / 64GB ECC |
+--------------------------------------------------------------+
```
| Hostname | Role / Operating System | Hardware Specifications | Nominal Load |
| :--- | :--- | :--- | :--- |
| **`gw-node01.lan`** | Edge Gateway / OPNsense 24.7 | Intel N100 (4C/4T), 16GB DDR5, 4x 2.5GbE I226-V | CPU: 18% \| RAM: 32% |
| **`pve-node02.lan`** | Hypervisor / Proxmox VE 8.2 | AMD Ryzen 9 5900X (12C/24T), 128GB ECC DDR4, 2x 2TB NVMe | CPU: 42% \| RAM: 68% |
| **`nas-node03.lan`** | Storage / TrueNAS SCALE | Intel Core i5-12400 (6C/12T), 64GB ECC, 6x 16TB Enterprise SAS | CPU: 14% \| RAM: 82% (ZFS ARC) |
---
## 6. ASCII Mascot & Daemon Artframes (`sys-daemon`)
### Frame 1: Idle / Scanning
```text
.-----------------.
/ ^ ^ ^ /|
/ (_) (_) (_) / |
/__________________/ |
| ___ ___ | |
| [o_o] [o_o] | |
| `-' `-' | |
| ______ | |
| / \ | /
| | -- | | /
| \____/ |/
`------------------'
```
### Frame 2: Active / Alert
```text
.-----------------.
/ ^ ^ ^ /|
/ (_) (_) (_) / |
/__________________/ |
| ___ ___ | |
| [-_-] [-_-] | |
| `-' `-' | |
| ______ | |
| / \ | /
| | O | | /
| \____/ |/
`------------------'
```
### Frame 3: Scroll-Tracker Cat Daemon
```text
/\___/\
( o o ) sys-daemon
( =^= ) [TRACKING]
(---)
```
---
## 7. Sample Pages Material Mapping Matrix
This matrix specifies which components and data entries from this canonical document are presented in each sample layout:
| Sample File | Layout Style | Persona / Bio | Services Included | Notes / Articles | Project Modules | Nodes & Telemetry | Mascot / Daemon |
| :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
| [`index.html`](file:///home/venus/code/blog/index.html) | Root Minimalist Business Card | Full whoami | — | — | Active Stack Tags | Terminal stdout | — |
| [`samples/homepage.html`](file:///home/venus/code/blog/samples/homepage.html) | Sample 1: Developer Homepage | Profile header | — | Post 1 (Full) + Archive Index (2,3,4) | — | — | — |
| [`samples/split-panel.html`](file:///home/venus/code/blog/samples/split-panel.html) | Sample 2: Split Panel Feed | — | — | — | Modules 1–5 | Status row | Frame 1/2 Animated |
| [`samples/companion.html`](file:///home/venus/code/blog/samples/companion.html) | Sample 3: Companion Layout | — | — | — | Modules 1–5 | Terminal card | Frame 3 Cat Daemon |
| [`samples/document-flow.html`](file:///home/venus/code/blog/samples/document-flow.html) | Sample 4: Continuous Document Flow | Header banner | — | Post 1 Snippets | Modules 1–5 in detail | — | Frame 3 Scroll Tracker |
| [`samples/portfolio.html`](file:///home/venus/code/blog/samples/portfolio.html) | Sample 5: Portfolio Card | Full whoami | — | — | Active Stack Tags | Terminal stdout | — |
| [`samples/homelab.html`](file:///home/venus/code/blog/samples/homelab.html) | Sample 6: Homelab Lander | Header banner | All Core & Compute (12 services) | — | — | Nodes 1–3 + ASCII Topology | Terminal logs |
| [`samples/public-services.html`](file:///home/venus/code/blog/samples/public-services.html) | Sample 7: Public Portal | Header banner | All Public Utilities (9 services) | — | — | Keys & PGP box | — |
| [`samples/sample-dashboard.html`](file:///home/venus/code/blog/samples/sample-dashboard.html) | Sample 8: 3-Tile Workspace | Center tile bio | Left tile services | Right tile archive | Center tile projects | Status bar | Vim command bar |
| [`samples/sample-command-center.html`](file:///home/venus/code/blog/samples/sample-command-center.html) | Sample 9: Tabbed Command Center | Tab 3 (Resume) | Tab 1 (Services) | Tab 2 (Blog Notes) | Tab 3 (Projects) | Tab 1 (Nodes) | Status dots |
| [`samples/sample-dual-pane-explorer.html`](file:///home/venus/code/blog/samples/sample-dual-pane-explorer.html) | Sample 10: Helix Tree Explorer | Sidebar links | Services subtree | Notes subtree | Modules subtree | Status bar | Sidebar companion |
-475
View File
@@ -1,475 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample 3: Companion Layout</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
/* Design Spec Token Definition */
:root {
--bg-canvas: #0d0f12; /* Deep charcoal slate */
--bg-surface: #121417; /* Slightly lighter dark surface */
--border-color: #2b303b; /* Low contrast static borders */
--text-primary: #e0e6f0; /* Off-white prose */
--text-muted: #8a95a5; /* Timestamp/status information */
--accent: #00f0ff; /* High-contrast active Cyan */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.6;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* Sticky Navigation Header at the top */
.sticky-header {
position: sticky;
top: 0;
height: 56px;
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
z-index: 100;
font-family: 'IBM Plex Mono', monospace;
}
.logo-container {
font-weight: 700;
font-size: 0.95rem;
color: var(--text-primary);
}
.logo-prompt {
color: var(--accent);
}
.header-nav {
display: flex;
gap: 16px;
}
.nav-link {
color: var(--text-primary);
text-decoration: none;
font-size: 0.85rem;
padding: 4px 10px;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
transition: none; /* No spring/delayed transitions */
}
/* Snappy Hover Inversion */
.nav-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.nav-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
/* Structural Indicator for Active Navigation Items */
.nav-link.active::before {
content: "> ";
color: var(--accent);
margin-right: 4px;
}
.nav-link.active:hover::before {
color: var(--bg-canvas);
}
/* Split Container Layout */
.split-container {
display: flex;
flex: 1;
height: calc(100vh - 56px);
overflow: hidden;
}
/* Left Pane - Scrollable Content Feed */
.left-pane {
width: 50%;
overflow-y: auto;
background-color: var(--bg-canvas);
padding: 40px;
border-right: 1px solid var(--border-color);
}
/* Right Pane - Fixed Centered ASCII Widget */
.right-pane {
width: 50%;
background-color: var(--bg-canvas);
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
overflow: hidden;
}
.scroll-content {
width: 100%;
max-width: 65ch; /* Cap article readability width */
margin: 0 auto;
}
.section-title {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
font-size: 1.3rem;
color: var(--text-primary);
margin-bottom: 24px;
border-bottom: 1px solid var(--border-color);
padding-bottom: 8px;
}
/* Cards Feed Component */
.card-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.project-card {
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 20px;
background-color: var(--bg-canvas); /* inherits canvas background */
cursor: pointer;
outline: none;
transition: none; /* Interaction suppression */
}
/* Instant Typography Snap on Hover/Focus */
.project-card:hover .card-title,
.project-card:focus .card-title {
color: var(--accent);
}
.project-card:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.card-title {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
font-size: 1rem;
color: var(--text-primary);
margin-bottom: 8px;
transition: none;
}
.card-description {
font-size: 0.9rem;
color: var(--text-muted);
line-height: 1.5;
}
/* Companion Terminal Widget */
.companion-terminal {
width: 100%;
max-width: 420px;
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
font-family: 'IBM Plex Mono', monospace;
overflow: hidden;
}
.terminal-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.8rem;
color: var(--text-muted);
}
.tab-title {
color: var(--text-primary);
font-weight: 500;
}
.tab-status {
font-weight: 700;
color: var(--accent);
}
.terminal-body {
padding: 20px;
}
.mascot-pre {
font-family: 'JetBrains Mono', monospace;
font-size: 0.85rem;
line-height: 1.35;
color: var(--text-primary);
background-color: var(--bg-canvas);
padding: 16px;
border: 1px solid var(--border-color);
border-radius: 2px;
overflow: hidden;
white-space: pre;
margin-bottom: 16px;
}
/* Terminal Blinking Block Cursor */
.cursor-block {
display: inline-block;
width: 8px;
height: 15px;
background-color: var(--text-primary);
animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
to {
visibility: hidden;
}
}
.status-log-panel {
border-top: 1px solid var(--border-color);
padding-top: 12px;
font-size: 0.75rem;
}
.log-output {
color: var(--text-muted);
word-break: break-all;
}
/* Footer styling */
.pane-footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
/* Mobile Layout (Width <= 768px) */
@media (max-width: 768px) {
body {
overflow-y: auto;
height: auto;
}
.split-container {
flex-direction: column;
height: auto;
overflow: visible;
}
.right-pane {
width: 100%;
height: 300px;
padding: 20px;
order: 1; /* Companion moves to top of stack */
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 56px;
z-index: 90;
}
.left-pane {
width: 100%;
height: auto;
overflow-y: visible;
padding: 24px;
order: 2; /* Content feed follows below */
border-right: none;
}
.companion-terminal {
max-width: 100%;
height: 100%;
}
.terminal-body {
padding: 12px;
display: flex;
flex-direction: column;
height: calc(100% - 33px);
}
.mascot-pre {
padding: 8px;
font-size: 0.75rem;
line-height: 1.25;
flex: 1;
margin-bottom: 8px;
}
.status-log-panel {
padding-top: 8px;
}
}
</style>
</head>
<body>
<!-- Sticky Top Header -->
<header class="sticky-header">
<div class="logo-container">
<span class="logo-prompt">$</span> agy --layout=companion
</div>
<nav class="header-nav">
<a href="index.html" class="nav-link">Catalog</a>
<a href="homepage.html" class="nav-link">Sample 1</a>
<a href="split-panel.html" class="nav-link">Sample 2</a>
<a href="companion.html" class="nav-link active">Sample 3</a>
<a href="document-flow.html" class="nav-link">Sample 4</a>
<a href="portfolio.html" class="nav-link">Sample 5</a>
<a href="homelab.html" class="nav-link">Sample 6</a>
<a href="public-services.html" class="nav-link">Sample 7</a>
<a href="sample-dashboard.html" class="nav-link">Sample 8</a>
<a href="sample-command-center.html" class="nav-link">Sample 9</a>
<a href="sample-dual-pane-explorer.html" class="nav-link">Sample 10</a>
</nav>
</header>
<!-- Split View Container -->
<div class="split-container">
<!-- Left Pane: Scrollable Project Cards -->
<main class="left-pane" aria-label="Project Feed">
<div class="scroll-content">
<h2 class="section-title">River Rooks // System Modules</h2>
<div class="card-list">
<!-- Card 1 -->
<div class="project-card" tabindex="0" data-target="/dev/posix-shell-parser">
<h3 class="card-title">Custom POSIX Shell Parser <span style="font-size: 0.8rem; font-weight: 400; color: var(--text-muted);">[v1.2.0 • #lua]</span></h3>
<p class="card-description">
A lightweight Lexer and AST parser written in Lua for parsing POSIX-compliant shell scripts. Highly optimized for syntax coloring inside modal environments without runtime heap allocations.
</p>
</div>
<!-- Card 2 -->
<div class="project-card" tabindex="0" data-target="/dev/wireguard-mesh">
<h3 class="card-title">WireGuard Mesh Topology Builder <span style="font-size: 0.8rem; font-weight: 400; color: var(--text-muted);">[v0.8.4 • #wireguard]</span></h3>
<p class="card-description">
Automated configuration generator for computing key matrices and setting up full-mesh WireGuard private overlays across cloud instances and local homelab nodes.
</p>
</div>
<!-- Card 3 -->
<div class="project-card" tabindex="0" data-target="/dev/zero-alloc-logger">
<h3 class="card-title">Zero-Allocation Logger (Go) <span style="font-size: 0.8rem; font-weight: 400; color: var(--text-muted);">[v2.1.1 • #golang]</span></h3>
<p class="card-description">
A high-performance JSON structure logger that uses sync.Pool buffers to avoid memory allocation overhead during intensive disk operations (>500k ops/sec).
</p>
</div>
<!-- Card 4 -->
<div class="project-card" tabindex="0" data-target="/dev/kernel-bootstrap">
<h3 class="card-title">Embedded Kernel Boot Strap <span style="font-size: 0.8rem; font-weight: 400; color: var(--text-muted);">[v0.1.0 • #arm-cortex]</span></h3>
<p class="card-description">
Minimalist boot sector and initial kernel bootstrap for ARM Cortex-M microcontrollers to handle vector table offset mapping and early UART status printouts.
</p>
</div>
<!-- Card 5 -->
<div class="project-card" tabindex="0" data-target="/dev/ddns-daemon">
<h3 class="card-title">Traefik Cloudflare DDNS Daemon <span style="font-size: 0.8rem; font-weight: 400; color: var(--text-muted);">[v1.0.2 • #docker-traefik]</span></h3>
<p class="card-description">
Background controller daemon designed to sync Docker container network labels with Cloudflare DNS records to route traffic to local homelab subdomains dynamically.
</p>
</div>
</div>
<footer class="pane-footer">
<span>Sample 3: Companion Layout</span>
<span>© 2026 River Rooks</span>
</footer>
</div>
</main>
<!-- Right Pane: Centered ASCII Companion -->
<aside class="right-pane" aria-label="System Companion">
<div class="companion-terminal">
<div class="terminal-header">
<span class="tab-title">sys-daemon</span>
<span class="tab-status">ACTIVE</span>
</div>
<div class="terminal-body">
<pre id="mascot-display" class="mascot-pre"></pre>
<div class="status-log-panel">
<div id="log-output" class="log-output"></div>
</div>
</div>
</div>
</aside>
</div>
<!-- Companion State Script -->
<script>
const IDLE_ART = ` /\\___/\\
( o o ) sys-daemon
( =^= ) STATUS: IDLE
\\__*__/ LOG: awaiting input<span class="cursor-block"></span>`;
const READING_ART = ` /\\___/\\
( - - ) sys-daemon
( =o= ) STATUS: READING
\\__*__/ LOG: reading module_`;
const mascot = document.getElementById('mascot-display');
const logOutput = document.getElementById('log-output');
const cards = document.querySelectorAll('.project-card');
function setIdle() {
mascot.innerHTML = IDLE_ART;
logOutput.innerText = `> sys-daemon initialized`;
}
function setReading(cardTarget) {
mascot.innerHTML = READING_ART;
logOutput.innerText = `[sys-daemon] target: ${cardTarget}`;
}
// Set Initial State
setIdle();
// Event hooks for Instant State swaps
cards.forEach(card => {
const target = card.getAttribute('data-target');
// Mouse triggers
card.addEventListener('mouseenter', () => setReading(target));
card.addEventListener('mouseleave', setIdle);
// Keyboard accessibility focus/blur triggers
card.addEventListener('focus', () => setReading(target));
card.addEventListener('blur', setIdle);
});
</script>
</body>
</html>
-81
View File
@@ -1,81 +0,0 @@
# Project Brief: Technical Web Architecture & Blog
**Project Type:** Personal Blog & Technical Documentation Site
**Design Philosophy:** High-signal, low-noise. Developer-centric minimalism.
**Vibe/Comparisons:** Zed Editor, Ghostty, Noctalia.dev.
**Strictly Avoid:** Raycast, Astro, Vercel-style "SaaS" aesthetics.
## 1. Executive Summary
This project requires a web interface that feels like a native development environment or terminal emulator, not a marketing page. The design must prioritize raw performance, keyboard navigability, and information density. It should evoke the craftsmanship of tiling window managers and modal text editors.
The content will heavily feature deep technical writing, including CTF write-ups, network routing diagrams, Docker/Traefik architectures, and raw code snippets (Lua, POSIX `sh`, and Python). The design must accommodate high-density information without visual clutter.
---
## 2. Global Rules & Anti-Patterns
To achieve the correct aesthetic, the designer must adhere to strict constraints.
> **The "Do Not Use" List**
> * **NO Glassmorphism:** Zero use of `backdrop-filter: blur()`, frosted glass, or translucent overlays.
> * **NO Glows or Drop Shadows:** Elements should not float, glow, or cast shadows. Depth is created through crisp 1px borders and distinct surface colors.
> * **NO Gradients:** Avoid sweeping linear or radial gradients. Stick to solid colors.
> * **NO Spring Animations:** Avoid floaty, delayed transitions.
> * **NO Rounded Blobs:** Keep border-radii minimal (0px to 4px max). Sharp corners are preferred.
---
## 3. Typography System
The typographical hierarchy is a hybrid system utilizing monospace fonts for structural elements and a clean sans-serif for long-form reading.
| Usage | Font Family Preference | Weight | Role |
| :--- | :--- | :--- | :--- |
| **Structural / UI** | IBM Plex Mono, JetBrains Mono | Regular (400), Medium (500) | Navigation links, metadata, dates, tags, timestamps, buttons. |
| **Code Blocks** | JetBrains Mono, Fira Code | Regular (400) | Inline code snippets, terminal command blocks, scripts. |
| **Body Prose** | Inter, Geist Sans, Roboto | Regular (400) | Standard article paragraphs, lists. |
| **Headings (H1-H3)**| IBM Plex Mono (or Sans variant) | Bold (700) | Article titles, section headers. |
**Typographical Constraints:**
* **Line Height:** `1.6` to `1.7` for prose.
* **Max Width:** Cap article text at `65ch` (characters) for optimal readability.
---
## 4. Color Palette (Dark Mode Native)
The site does not need a light mode. It should be built as a dark-mode native experience using a muted, low-saturation charcoal palette, mimicking a modern terminal or IDE.
* **Base Canvas (`#121417` or `#0d0f12`):** A deep slate/charcoal. Not pure black (`#000000`), which causes eye strain.
* **Surface / Card (`#1c1f26`):** Slightly lighter than the canvas for article blocks or terminal windows.
* **Borders & Dividers (`#2b303b`):** Subtle, low-contrast 1px solid lines used to define grids and separate content.
* **Primary Text (`#e0e6f0`):** Off-white/light ash for prose.
* **Muted Text (`#8a95a5`):** For timestamps, metadata, and non-essential UI text.
* **Accent Color (Designer's Choice):** One single, vibrant terminal color (e.g., ANSI Green, Amber, or Cyan) used sparingly for active states, link hovers, or terminal prompts (like `>`).
---
## 5. Layout & Component Architecture
The layout should be heavily grid-based, feeling structural and deliberate.
### Core Components Needed
1. **Terminal-Style Code Blocks:** Must support syntax highlighting. Should include a simple header (e.g., showing the filename like `config.lua` or `docker-compose.yml`) with a 1px border.
2. **Metadata Tags:** Small, monospaced inline blocks for categorizing posts (e.g., `[#homelab]`, `[#ctf]`, `[#hyprland]`).
3. **Keyboard Shortcut Hints:** Visual styling for `<kbd>` elements, rendering as crisp, bordered boxes (e.g., `Ctrl` + `K`).
4. **Index/Archive List:** A dense, tabular list for older posts, prioritized by date, title, and tags, rather than large image thumbnails.
### Spacing & Grid
* Use a rigid 4pt or 8pt baseline grid.
* Padding inside code blocks and cards should be dense but mathematically consistent.
* Full-bleed horizontal dividers (`<hr>`) should be used to separate major sections instead of large margins.
---
## 6. Interaction & Motion
Interactivity should mirror command-line interfaces: fast, binary, and unembellished.
* **Hover States:** Immediate state changes. Instead of fading a button's background over `300ms`, snap the background color instantly or invert the text/background colors.
* **Focus States:** Explicit, high-visibility 1px or 2px solid outlines (using the accent color) when navigating via the `Tab` key. Accessibility and keyboard navigation are paramount.
* **Active Elements:** Use structural indicators (like a `>` prepended to an active navigation link, or a solid left-border block) rather than changing font sizes or scaling elements up.
-609
View File
@@ -1,609 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample 4: Document Flow Layout</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
/* Design Tokens */
:root {
--bg-canvas: #0d0f12; /* Deep charcoal slate */
--bg-surface: #121417; /* Slightly lighter dark surface */
--border-color: #2b303b; /* Low-contrast static 1px borders */
--text-primary: #e0e6f0; /* Off-white reading text */
--text-muted: #8a95a5; /* Monospaced metadata / timestamps */
--accent: #00f0ff; /* High-contrast Cyan indicator */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.65;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* Sticky Navigation Header */
.sticky-header {
position: sticky;
top: 0;
height: 56px;
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
z-index: 100;
font-family: 'IBM Plex Mono', monospace;
}
.logo-container {
font-weight: 700;
font-size: 0.95rem;
color: var(--text-primary);
}
.logo-prompt {
color: var(--accent);
}
.header-nav {
display: flex;
gap: 16px;
}
.nav-link {
color: var(--text-primary);
text-decoration: none;
font-size: 0.85rem;
padding: 4px 10px;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
transition: none;
}
.nav-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.nav-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.nav-link.active::before {
content: "> ";
color: var(--accent);
margin-right: 4px;
}
.nav-link.active:hover::before {
color: var(--bg-canvas);
}
/* Main Split Layout */
.split-container {
display: flex;
flex: 1;
height: calc(100vh - 56px);
overflow: hidden;
}
/* Left Pane - Fixed Terminal Scroll-Tracker Daemon */
.left-pane {
width: 380px;
background-color: var(--bg-canvas);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 32px;
flex-shrink: 0;
}
.companion-terminal {
width: 100%;
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
font-family: 'IBM Plex Mono', monospace;
overflow: hidden;
}
.terminal-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.8rem;
color: var(--text-muted);
}
.tab-title {
color: var(--text-primary);
font-weight: 500;
}
.tab-status {
font-weight: 700;
color: var(--accent);
}
.terminal-body {
padding: 20px;
}
.mascot-pre {
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
line-height: 1.35;
color: var(--text-primary);
background-color: var(--bg-canvas);
padding: 16px;
border: 1px solid var(--border-color);
border-radius: 2px;
overflow: hidden;
white-space: pre;
margin-bottom: 16px;
}
/* Blinking Block Cursor */
.cursor-block {
display: inline-block;
width: 8px;
height: 15px;
background-color: var(--text-primary);
animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
to {
visibility: hidden;
}
}
.status-log-panel {
border-top: 1px solid var(--border-color);
padding-top: 12px;
font-size: 0.75rem;
}
.log-output {
color: var(--text-muted);
word-break: break-all;
}
/* Right Pane - Continuous Document Stream */
.right-pane {
flex: 1;
overflow-y: auto;
background-color: var(--bg-canvas);
padding: 48px 32px;
display: flex;
justify-content: center;
scroll-behavior: smooth;
}
.document-stream {
width: 100%;
max-width: 65ch; /* Optimal prose line length */
}
.document-header {
margin-bottom: 40px;
padding-bottom: 24px;
border-bottom: 1px solid var(--border-color);
}
.doc-title {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
font-size: 1.8rem;
color: var(--text-primary);
margin-bottom: 12px;
}
.doc-subtitle {
font-size: 1rem;
color: var(--text-muted);
line-height: 1.5;
}
/* Continuous Section Flow (No 4-sided Bounding Boxes) */
.doc-section {
padding: 32px 0;
border-bottom: 1px solid var(--border-color);
scroll-margin-top: 20px;
}
.doc-section:last-of-type {
border-bottom: none;
}
.section-header-wrap {
display: flex;
align-items: baseline;
gap: 10px;
margin-bottom: 8px;
}
/* Structural Indicator for Active Scroll Section */
.active-indicator {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
color: var(--accent);
opacity: 0;
transition: none;
}
.doc-section.is-active .active-indicator {
opacity: 1;
}
.section-heading {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
font-size: 1.25rem;
color: var(--text-primary);
}
.doc-section.is-active .section-heading {
color: var(--accent);
}
.section-meta {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
margin-bottom: 16px;
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.meta-tag {
color: var(--text-muted);
}
.doc-section p {
color: var(--text-primary);
font-size: 0.95rem;
margin-bottom: 16px;
}
.doc-section p:last-child {
margin-bottom: 0;
}
/* Terminal Code Block Component */
.code-block {
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
margin: 20px 0;
font-family: 'JetBrains Mono', monospace;
overflow: hidden;
}
.code-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 6px 14px;
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: var(--text-muted);
}
.code-content {
padding: 14px;
overflow-x: auto;
font-size: 0.82rem;
line-height: 1.5;
}
.code-content pre {
margin: 0;
}
.pane-footer {
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border-color);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
/* Responsive Mobile Layout (Width <= 768px) */
@media (max-width: 768px) {
body {
overflow-y: auto;
height: auto;
}
.split-container {
flex-direction: column;
height: auto;
overflow: visible;
}
.left-pane {
width: 100%;
height: 280px;
padding: 16px;
border-right: none;
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 56px;
z-index: 90;
}
.right-pane {
padding: 24px 16px;
overflow-y: visible;
}
}
</style>
</head>
<body>
<!-- Sticky Navigation Header -->
<header class="sticky-header">
<div class="logo-container">
<span class="logo-prompt">$</span> agy --layout=document-flow
</div>
<nav class="header-nav">
<a href="index.html" class="nav-link">Catalog</a>
<a href="homepage.html" class="nav-link">Sample 1</a>
<a href="split-panel.html" class="nav-link">Sample 2</a>
<a href="companion.html" class="nav-link">Sample 3</a>
<a href="document-flow.html" class="nav-link active">Sample 4</a>
<a href="portfolio.html" class="nav-link">Sample 5</a>
<a href="homelab.html" class="nav-link">Sample 6</a>
<a href="public-services.html" class="nav-link">Sample 7</a>
<a href="sample-dashboard.html" class="nav-link">Sample 8</a>
<a href="sample-command-center.html" class="nav-link">Sample 9</a>
<a href="sample-dual-pane-explorer.html" class="nav-link">Sample 10</a>
</nav>
</header>
<!-- Split View Container -->
<div class="split-container">
<!-- Left Pane: Fixed Terminal Scroll-Tracker Daemon -->
<aside class="left-pane" aria-label="Scroll Tracker Daemon">
<div class="companion-terminal">
<div class="terminal-header">
<span class="tab-title">sys-daemon (scroll-tracker)</span>
<span class="tab-status">ONLINE</span>
</div>
<div class="terminal-body">
<pre id="mascot-display" class="mascot-pre"></pre>
<div class="status-log-panel">
<div id="log-output" class="log-output">> continuous document flow initialized</div>
</div>
</div>
</div>
</aside>
<!-- Right Pane: Continuous Document Reading Stream -->
<main id="scroll-target" class="right-pane" aria-label="Technical Documentation">
<div class="document-stream">
<!-- Document Header -->
<header class="document-header">
<h1 class="doc-title">River Rooks // Systems &amp; Architecture</h1>
<p class="doc-subtitle">
A high-density technical reading flow. Pinned left pane contains an automated terminal tracker daemon observing scroll depth across system modules.
</p>
</header>
<!-- Section 1 -->
<section id="section-shell-parser" class="doc-section" data-target="/dev/posix-shell-parser">
<div class="section-header-wrap">
<span class="active-indicator">></span>
<h2 class="section-heading">Custom POSIX Shell Parser</h2>
</div>
<div class="section-meta">
<span>Version: v1.2.0</span>
<span>•</span>
<span>Module: /dev/posix-shell-parser</span>
<span>•</span>
<span class="meta-tag">[#lua]</span>
<span class="meta-tag">[#ast-parser]</span>
</div>
<p>
This section details a lightweight Lexer and AST parser written in Lua for parsing POSIX-compliant shell scripts. The architecture avoids runtime allocations during tokenization, enabling real-time syntax coloring inside terminal buffers.
</p>
<p>
The execution pipeline operates in two distinct phases: string scanning into token stream vectors, followed by recursive descent operator precedence tree construction.
</p>
<div class="code-block">
<div class="code-header">
<span>lexer.lua</span>
<span>Lua</span>
</div>
<div class="code-content">
<pre><code>local function tokenize(src)
local tokens = {}
for pos, char in ipairs(src) do
if char == ";" or char == "|" then
table.insert(tokens, { type = "OPERATOR", value = char })
end
end
return tokens
end</code></pre>
</div>
</div>
</section>
<!-- Section 2 -->
<section id="section-wireguard-mesh" class="doc-section" data-target="/dev/wireguard-mesh">
<div class="section-header-wrap">
<span class="active-indicator">></span>
<h2 class="section-heading">WireGuard Mesh Topology Builder</h2>
</div>
<div class="section-meta">
<span>Version: v0.8.4</span>
<span>•</span>
<span>Module: /dev/wireguard-mesh</span>
<span>•</span>
<span class="meta-tag">[#networking]</span>
<span class="meta-tag">[#wireguard]</span>
</div>
<p>
Automated configuration generator designed to provision full-mesh WireGuard overlay networks across distributed cloud nodes and homelab infrastructure.
</p>
<p>
Instead of manually calculating public/private keypairs and Endpoint IP pairings per host node, this utility computes matrix topologies and generates atomic configuration files.
</p>
</section>
<!-- Section 3 -->
<section id="section-zero-alloc-logger" class="doc-section" data-target="/dev/zero-alloc-logger">
<div class="section-header-wrap">
<span class="active-indicator">></span>
<h2 class="section-heading">Zero-Allocation Logger (Go)</h2>
</div>
<div class="section-meta">
<span>Version: v2.1.1</span>
<span>•</span>
<span>Module: /dev/zero-alloc-logger</span>
<span>•</span>
<span class="meta-tag">[#go]</span>
<span class="meta-tag">[#performance]</span>
</div>
<p>
A high-throughput structured JSON logger implemented in Go that leverages <code>sync.Pool</code> byte buffers to completely eliminate heap allocations during intensive I/O operations.
</p>
<p>
Benchmark comparisons demonstrate zero GC overhead when processing over 500,000 log events per second across multi-threaded worker routines.
</p>
</section>
<!-- Section 4 -->
<section id="section-kernel-bootstrap" class="doc-section" data-target="/dev/kernel-bootstrap">
<div class="section-header-wrap">
<span class="active-indicator">></span>
<h2 class="section-heading">Embedded Kernel Boot Strap</h2>
</div>
<div class="section-meta">
<span>Version: v0.1.0</span>
<span>•</span>
<span>Module: /dev/kernel-bootstrap</span>
<span>•</span>
<span class="meta-tag">[#arm-cortex]</span>
<span class="meta-tag">[#assembly]</span>
</div>
<p>
Bare-metal boot sector and initial C runtime setup for ARM Cortex-M microcontrollers. Handles vector table offset initialization, memory region copy routines, and low-level UART debugging initialization.
</p>
</section>
<!-- Section 5 -->
<section id="section-ddns-daemon" class="doc-section" data-target="/dev/ddns-daemon">
<div class="section-header-wrap">
<span class="active-indicator">></span>
<h2 class="section-heading">Traefik Cloudflare DDNS Daemon</h2>
</div>
<div class="section-meta">
<span>Version: v1.0.2</span>
<span>•</span>
<span>Module: /dev/ddns-daemon</span>
<span>•</span>
<span class="meta-tag">[#docker]</span>
<span class="meta-tag">[#traefik]</span>
</div>
<p>
Background controller daemon designed to sync Docker container network labels with Cloudflare DNS records to route traffic to local homelab subdomains dynamically.
</p>
</section>
<footer class="pane-footer">
<span>Sample 4: Document Flow Layout</span>
<span>© 2026 River Rooks</span>
</footer>
</div>
</main>
</div>
<!-- Scroll-Tracker IntersectionObserver Script -->
<script>
const IDLE_ART = ` /\\___/\\
( o o ) sys-daemon
( =^= ) STATUS: IDLE
\\__*__/ LOG: awaiting input<span class="cursor-block"></span>`;
const READING_ART = ` /\\___/\\
( - - ) sys-daemon
( =o= ) STATUS: TRACKING
\\__*__/ LOG: reading module_`;
const mascot = document.getElementById('mascot-display');
const logOutput = document.getElementById('log-output');
const sections = document.querySelectorAll('.doc-section');
const rightPane = document.getElementById('scroll-target');
function setIdle() {
mascot.innerHTML = IDLE_ART;
logOutput.innerText = `> sys-daemon (idle scroll-tracker)`;
}
function setTracking(targetPath) {
mascot.innerHTML = READING_ART;
logOutput.innerText = `[sys-daemon] active_section: ${targetPath}`;
}
setIdle();
// IntersectionObserver to observe document sections in scroll view
const observerOptions = {
root: rightPane,
rootMargin: '-20% 0px -60% 0px', // Active threshold zone
threshold: 0
};
let activeSection = null;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
if (activeSection) {
activeSection.classList.remove('is-active');
}
activeSection = entry.target;
activeSection.classList.add('is-active');
const target = activeSection.getAttribute('data-target');
setTracking(target);
}
});
}, observerOptions);
sections.forEach(section => observer.observe(section));
</script>
</body>
</html>
-871
View File
@@ -1,871 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Homelab Lander & Infrastructure Catalog</title>
<meta name="description" content="Public landing page and operational directory for River Rooks self-hosted homelab infrastructure, services, hardware nodes, and network topology.">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
:root {
--bg-canvas: #0d0f12; /* Deep slate/charcoal */
--bg-surface: #121417; /* Solid surface container */
--bg-surface-alt: #181b21; /* Slightly lighter surface card */
--border-color: #2b303b; /* Low-contrast 1px border */
--border-active: #00f0ff; /* Active Cyan border */
--text-primary: #e0e6f0; /* Off-white prose */
--text-muted: #8a95a5; /* Monospaced metadata gray */
--accent: #00f0ff; /* High-visibility Cyan terminal accent */
--accent-green: #00ff66; /* ANSI Green for online status */
--accent-amber: #ffb000; /* Amber for alerts / tags */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.65;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Sticky Navigation Header */
.sticky-header {
position: sticky;
top: 0;
height: 56px;
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
z-index: 100;
font-family: 'IBM Plex Mono', monospace;
}
.logo-container {
font-weight: 700;
font-size: 0.92rem;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.logo-prompt {
color: var(--accent);
}
.header-nav {
display: flex;
gap: 10px;
overflow-x: auto;
}
.nav-link {
color: var(--text-primary);
text-decoration: none;
font-size: 0.82rem;
padding: 4px 8px;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
transition: none; /* Instant snap */
white-space: nowrap;
}
.nav-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.nav-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.nav-link.active::before {
content: "> ";
color: var(--accent);
margin-right: 4px;
}
.nav-link.active:hover::before {
color: var(--bg-canvas);
}
/* Outer Wrapper */
.page-wrapper {
flex: 1;
display: flex;
justify-content: center;
padding: 40px 24px 60px 24px;
}
.content-container {
width: 100%;
max-width: 1000px;
}
/* Identity Header */
.identity-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 16px;
margin-bottom: 24px;
}
.header-titles {
flex: 1;
min-width: 280px;
}
.site-h1 {
font-family: 'IBM Plex Mono', monospace;
font-size: 2rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 6px;
letter-spacing: -0.02em;
}
.status-line {
display: flex;
align-items: center;
gap: 10px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
}
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent-green);
display: inline-block;
flex-shrink: 0;
}
.system-badge-box {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
padding: 10px 14px;
border-radius: 4px;
color: var(--text-muted);
line-height: 1.5;
}
.system-badge-box strong {
color: var(--text-primary);
}
/* Dividers */
.section-divider {
border: 0;
border-top: 1px solid var(--border-color);
margin: 32px 0;
}
/* Terminal Status Block */
.terminal-block {
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
font-family: 'JetBrains Mono', monospace;
overflow: hidden;
margin-bottom: 36px;
}
.terminal-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 8px 16px;
display: flex;
justify-content: space-between;
font-size: 0.78rem;
color: var(--text-muted);
}
.terminal-title {
color: var(--text-primary);
font-weight: 500;
}
.terminal-body {
padding: 16px;
font-size: 0.84rem;
line-height: 1.55;
}
.cmd-line {
color: var(--text-muted);
margin-bottom: 10px;
}
.prompt {
color: var(--accent);
font-weight: 700;
}
.cmd-output {
color: var(--text-primary);
white-space: pre-wrap;
}
.highlight-green {
color: var(--accent-green);
}
.highlight-cyan {
color: var(--accent);
}
.highlight-amber {
color: var(--accent-amber);
}
.cursor-block {
display: inline-block;
width: 8px;
height: 15px;
background-color: var(--text-primary);
margin-left: 4px;
vertical-align: middle;
animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
to { visibility: hidden; }
}
/* Section Headings */
.section-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 16px;
flex-wrap: wrap;
gap: 8px;
}
.section-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.15rem;
font-weight: 700;
color: var(--text-primary);
}
.section-meta {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
}
/* Interactive Filter Controls */
.filter-bar {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 24px;
font-family: 'IBM Plex Mono', monospace;
}
.filter-btn {
background-color: var(--bg-surface);
color: var(--text-muted);
border: 1px solid var(--border-color);
padding: 6px 12px;
font-size: 0.8rem;
cursor: pointer;
font-family: inherit;
border-radius: 4px;
transition: none; /* Instant snap */
}
.filter-btn:hover {
color: var(--text-primary);
border-color: var(--accent);
}
.filter-btn.active {
background-color: var(--accent);
color: var(--bg-canvas);
border-color: var(--accent);
font-weight: 700;
}
.filter-btn:focus {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
/* Services Card Grid */
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
margin-bottom: 36px;
}
.service-card {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 18px;
display: flex;
flex-direction: column;
justify-content: space-between;
transition: none;
}
.service-card:hover {
border-color: var(--border-active);
}
.service-card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px;
}
.service-name {
font-family: 'IBM Plex Mono', monospace;
font-size: 1rem;
font-weight: 700;
color: var(--text-primary);
}
.service-card:hover .service-name {
color: var(--accent);
}
.service-tag {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
}
.service-desc {
font-size: 0.88rem;
color: var(--text-muted);
margin-bottom: 16px;
line-height: 1.5;
}
.service-footer {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 10px;
border-top: 1px solid var(--border-color);
}
.service-port {
color: var(--text-muted);
}
.service-status {
display: flex;
align-items: center;
gap: 6px;
font-weight: 500;
}
.status-text-online {
color: var(--accent-green);
}
.status-text-active {
color: var(--accent);
}
/* Hardware Nodes & Metrics Table */
.nodes-container {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
overflow-x: auto;
margin-bottom: 36px;
}
.nodes-table {
width: 100%;
border-collapse: collapse;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.83rem;
text-align: left;
}
.nodes-table th {
background-color: var(--bg-canvas);
color: var(--text-primary);
border-bottom: 1px solid var(--border-color);
padding: 10px 14px;
font-weight: 700;
}
.nodes-table td {
padding: 12px 14px;
border-bottom: 1px solid var(--border-color);
color: var(--text-primary);
vertical-align: middle;
}
.nodes-table tr:last-child td {
border-bottom: none;
}
.nodes-table tr:hover td {
background-color: var(--bg-surface-alt);
}
.node-hostname {
font-weight: 700;
color: var(--accent);
}
.metric-bar {
color: var(--accent-green);
}
/* Network Topology ASCII Block */
.topology-block {
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
font-family: 'JetBrains Mono', monospace;
padding: 18px;
font-size: 0.8rem;
line-height: 1.4;
overflow-x: auto;
color: var(--text-muted);
margin-bottom: 36px;
}
.topology-title {
color: var(--text-primary);
font-weight: 700;
margin-bottom: 12px;
}
/* Piped Footer Links */
.footer-links {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.footer-link {
color: var(--text-primary);
text-decoration: none;
padding: 2px 6px;
transition: none; /* Zero-delay snap */
}
.footer-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.footer-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.pipe {
color: var(--border-color);
}
</style>
</head>
<body>
<!-- Sticky Header -->
<header class="sticky-header">
<div class="logo-container">
<span class="logo-prompt">$</span> agy --page=homelab
</div>
<nav class="header-nav" aria-label="Main Navigation">
<a href="index.html" class="nav-link">Catalog</a>
<a href="homepage.html" class="nav-link">Sample 1</a>
<a href="split-panel.html" class="nav-link">Sample 2</a>
<a href="companion.html" class="nav-link">Sample 3</a>
<a href="document-flow.html" class="nav-link">Sample 4</a>
<a href="portfolio.html" class="nav-link">Sample 5</a>
<a href="homelab.html" class="nav-link active">Sample 6 (Homelab)</a>
<a href="public-services.html" class="nav-link">Sample 7</a>
<a href="sample-dashboard.html" class="nav-link">Sample 8</a>
<a href="sample-command-center.html" class="nav-link">Sample 9</a>
<a href="sample-dual-pane-explorer.html" class="nav-link">Sample 10</a>
</nav>
</header>
<!-- Page Content Container -->
<div class="page-wrapper">
<main class="content-container">
<!-- Identity & Status Banner -->
<header class="identity-header">
<div class="header-titles">
<h1 class="site-h1">Homelab Lander &amp; Infrastructure</h1>
<div class="status-line">
<span class="status-dot"></span>
<span>CLUSTER ONLINE | 10GbE BACKBONE | TRAEFIK V3 EDGE PROXY</span>
</div>
</div>
<div class="system-badge-box">
<div><strong>SYSTEM UPTIME:</strong> 99.98% (42d 14h)</div>
<div><strong>LOCATION:</strong> ALABAMA DCs / RACK-01</div>
</div>
</header>
<hr class="section-divider">
<!-- Under Construction / System Telemetry Terminal Block -->
<section aria-label="System Diagnostics Terminal">
<div class="terminal-block">
<div class="terminal-header">
<span class="terminal-title">/var/log/homelab-status.log</span>
<span>STDOUT [TTY1]</span>
</div>
<div class="terminal-body">
<div class="cmd-line"><span class="prompt">$</span> homelab-ctl status --output=summary</div>
<div class="cmd-output">[<span class="highlight-green">OK</span>] Edge Router: OPNsense 24.7 (10GbE SFP+ WAN/LAN)
[<span class="highlight-green">OK</span>] Reverse Proxy: Traefik v3.1 (Auto Let's Encrypt TLS Wildcard)
[<span class="highlight-green">OK</span>] Virtualization: Proxmox VE 8.2 Cluster (3 Bare-Metal Nodes)
[<span class="highlight-green">OK</span>] ZFS Array: TrueNAS SCALE (48TB RaidZ2 Pool, 0 Degradation)
[<span class="highlight-cyan">INFO</span>] Active Wireguard Mesh Peers: 14 Connected
[<span class="highlight-amber">WARN</span>] Sonarr Queue: 2 downloads pending verification<span class="cursor-block"></span></div>
</div>
</div>
</section>
<!-- Homelab Services Directory -->
<section aria-label="Services Directory">
<div class="section-header">
<h2 class="section-title">services_directory</h2>
<span class="section-meta">12 Active Micro-services</span>
</div>
<!-- Filter Bar -->
<div class="filter-bar" id="filterBar" role="tablist" aria-label="Filter Services">
<button class="filter-btn active" data-filter="all" role="tab" aria-selected="true">All Services</button>
<button class="filter-btn" data-filter="core" role="tab">Core &amp; Network</button>
<button class="filter-btn" data-filter="compute" role="tab">Compute &amp; Storage</button>
<button class="filter-btn" data-filter="media" role="tab">Media &amp; Automation</button>
<button class="filter-btn" data-filter="monitoring" role="tab">Monitoring &amp; Sec</button>
</div>
<!-- Services Cards Grid -->
<div class="services-grid" id="servicesGrid">
<!-- Traefik -->
<article class="service-card" data-category="core">
<div class="service-card-header">
<h3 class="service-name">Traefik Proxy</h3>
<span class="service-tag">[#core]</span>
</div>
<p class="service-desc">Edge HTTP reverse proxy and load balancer with dynamic Docker provider discovery and ACME TLS certificate management.</p>
<div class="service-footer">
<span class="service-port">443/tcp (TLS)</span>
<span class="service-status status-text-online"><span class="status-dot"></span> ONLINE</span>
</div>
</article>
<!-- OPNsense -->
<article class="service-card" data-category="core">
<div class="service-card-header">
<h3 class="service-name">OPNsense Firewall</h3>
<span class="service-tag">[#core]</span>
</div>
<p class="service-desc">Hardened FreeBSD firewall routing 10GbE traffic with VLAN isolation, Suricata IDS/IPS, and Unbound DNS.</p>
<div class="service-footer">
<span class="service-port">VLAN 10 / Mgmt</span>
<span class="service-status status-text-online"><span class="status-dot"></span> ACTIVE</span>
</div>
</article>
<!-- Proxmox VE -->
<article class="service-card" data-category="compute">
<div class="service-card-header">
<h3 class="service-name">Proxmox VE</h3>
<span class="service-tag">[#compute]</span>
</div>
<p class="service-desc">Enterprise hypervisor cluster hosting Debian LXC containers and KVM virtual machines with Ceph &amp; ZFS backend.</p>
<div class="service-footer">
<span class="service-port">8006/tcp</span>
<span class="service-status status-text-online"><span class="status-dot"></span> ONLINE</span>
</div>
</article>
<!-- TrueNAS SCALE -->
<article class="service-card" data-category="compute">
<div class="service-card-header">
<h3 class="service-name">TrueNAS SCALE</h3>
<span class="service-tag">[#storage]</span>
</div>
<p class="service-desc">Primary network attached storage managing 48TB ZFS RaidZ2 array, NFS shares, SMB datasets, and daily scrub jobs.</p>
<div class="service-footer">
<span class="service-port">NFS / SMB</span>
<span class="service-status status-text-online"><span class="status-dot"></span> ONLINE</span>
</div>
</article>
<!-- AdGuard Home -->
<article class="service-card" data-category="core">
<div class="service-card-header">
<h3 class="service-name">AdGuard Home</h3>
<span class="service-tag">[#dns]</span>
</div>
<p class="service-desc">Network-wide DNS sinkhole filtering trackers and ad domains across all VLANs with DNS-over-HTTPS upstream resolvers.</p>
<div class="service-footer">
<span class="service-port">53/udp, 853/tcp</span>
<span class="service-status status-text-active"><span class="status-dot"></span> PROTECTED</span>
</div>
</article>
<!-- WireGuard Mesh -->
<article class="service-card" data-category="core">
<div class="service-card-header">
<h3 class="service-name">WireGuard Mesh</h3>
<span class="service-tag">[#vpn]</span>
</div>
<p class="service-desc">High-performance encrypted point-to-point VPN tunnel connecting remote laptops and mobile endpoints to homelab subnet.</p>
<div class="service-footer">
<span class="service-port">51820/udp</span>
<span class="service-status status-text-online"><span class="status-dot"></span> 14 PEERS</span>
</div>
</article>
<!-- Jellyfin Media -->
<article class="service-card" data-category="media">
<div class="service-card-header">
<h3 class="service-name">Jellyfin Media</h3>
<span class="service-tag">[#media]</span>
</div>
<p class="service-desc">FOSS media streaming engine leveraging Intel QuickSync GPU hardware acceleration for 4K HDR transcoding.</p>
<div class="service-footer">
<span class="service-port">8096/tcp</span>
<span class="service-status status-text-online"><span class="status-dot"></span> READY</span>
</div>
</article>
<!-- Arr Automation Suite -->
<article class="service-card" data-category="media">
<div class="service-card-header">
<h3 class="service-name">Arr Stack (Sonarr/Radarr)</h3>
<span class="service-tag">[#automation]</span>
</div>
<p class="service-desc">Automated content indexing, Prowlarr torrent/Usenet management, and SABnzbd queue downloader pipeline.</p>
<div class="service-footer">
<span class="service-port">8989 / 7878</span>
<span class="service-status status-text-online"><span class="status-dot"></span> RUNNING</span>
</div>
</article>
<!-- Prometheus + Grafana -->
<article class="service-card" data-category="monitoring">
<div class="service-card-header">
<h3 class="service-name">Grafana &amp; Prometheus</h3>
<span class="service-tag">[#telemetry]</span>
</div>
<p class="service-desc">Time-series metric collection with Node Exporter, Telegraf agents, and live cluster performance dashboards.</p>
<div class="service-footer">
<span class="service-port">3000 / 9090</span>
<span class="service-status status-text-active"><span class="status-dot"></span> SCRAPING</span>
</div>
</article>
<!-- Uptime Kuma -->
<article class="service-card" data-category="monitoring">
<div class="service-card-header">
<h3 class="service-name">Uptime Kuma</h3>
<span class="service-tag">[#status]</span>
</div>
<p class="service-desc">Synthetic HTTP, Ping, and TCP health monitor broadcasting real-time service status alerts to Discord webhooks.</p>
<div class="service-footer">
<span class="service-port">3001/tcp</span>
<span class="service-status status-text-online"><span class="status-dot"></span> 100% HEALTH</span>
</div>
</article>
<!-- Vaultwarden -->
<article class="service-card" data-category="monitoring">
<div class="service-card-header">
<h3 class="service-name">Vaultwarden</h3>
<span class="service-tag">[#security]</span>
</div>
<p class="service-desc">Lightweight Rust implementation of the Bitwarden API hosting encrypted credentials, SSH keys, and TOTP tokens.</p>
<div class="service-footer">
<span class="service-port">443/tcp (E2EE)</span>
<span class="service-status status-text-active"><span class="status-dot"></span> LOCKED</span>
</div>
</article>
<!-- Forgejo Git -->
<article class="service-card" data-category="compute">
<div class="service-card-header">
<h3 class="service-name">Forgejo Git Server</h3>
<span class="service-tag">[#git]</span>
</div>
<p class="service-desc">Self-hosted Git forge hosting homelab infrastructure-as-code repos, Ansible playbooks, and Docker compose files.</p>
<div class="service-footer">
<span class="service-port">3000/tcp, 2222/ssh</span>
<span class="service-status status-text-online"><span class="status-dot"></span> ONLINE</span>
</div>
</article>
</div>
</section>
<hr class="section-divider">
<!-- Bare-Metal Hardware Nodes Grid -->
<section aria-label="Hardware Topology">
<div class="section-header">
<h2 class="section-title">hardware_nodes</h2>
<span class="section-meta">3 Physical Bare-Metal Servers</span>
</div>
<div class="nodes-container">
<table class="nodes-table">
<thead>
<tr>
<th>Hostname</th>
<th>Role &amp; OS</th>
<th>Hardware Spec</th>
<th>CPU Load</th>
<th>RAM Util</th>
</tr>
</thead>
<tbody>
<tr>
<td class="node-hostname">gw-node01.lan</td>
<td>OPNsense 24.7</td>
<td>Intel N100 (4C/4T) | 16GB DDR5 | 2.5GbE</td>
<td><span class="metric-bar">[██░░░░░░░░]</span> 18%</td>
<td><span class="metric-bar">[███░░░░░░░]</span> 32%</td>
</tr>
<tr>
<td class="node-hostname">pve-node02.lan</td>
<td>Proxmox VE 8.2</td>
<td>AMD Ryzen 9 5900X (12C/24T) | 128GB ECC | NVMe</td>
<td><span class="metric-bar">[████░░░░░░]</span> 42%</td>
<td><span class="metric-bar">[███████░░░]</span> 68%</td>
</tr>
<tr>
<td class="node-hostname">nas-node03.lan</td>
<td>TrueNAS SCALE</td>
<td>Intel Core i5-12400 (6C/12T) | 64GB ECC | 6x 16TB RaidZ2</td>
<td><span class="metric-bar">[██░░░░░░░░]</span> 14%</td>
<td><span class="metric-bar">[████████░░]</span> 82%</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Network Topology Diagram -->
<section aria-label="Network Architecture">
<div class="section-header">
<h2 class="section-title">network_topology</h2>
<span class="section-meta">ASCII Architecture Map</span>
</div>
<div class="topology-block">
<div class="topology-title">// Network Edge &amp; Traffic Routing Flow</div>
[ WAN / INTERNET ]
│
▼
┌─────────────────────────┐
│ OPNsense Edge Gateway │ (10GbE SFP+ / Firewall / IDS)
└───────────┬─────────────┘
│
├───────────────► [ WireGuard Mesh VPN ] (51820/udp Remote Access)
│
▼
┌─────────────────────────┐
│ Traefik v3 Reverse Proxy│ (Auto TLS Certs / Dynamic Docker)
└───────────┬─────────────┘
│
┌─────────┴───────────────────────┬─────────────────────────┐
▼ ▼ ▼
┌─────────────────────────┐ ┌───────────────────────┐ ┌─────────────────────────┐
│ VLAN 10: Management │ │ VLAN 20: Services │ │ VLAN 30: Storage Pool │
│ • Proxmox VE (8006) │ │ • Grafana (3000) │ │ • TrueNAS ZFS (SMB/NFS) │
│ • AdGuard Home (53) │ │ • Jellyfin Media │ │ • Forgejo Git (3000) │
└─────────────────────────┘ └───────────────────────┘ └─────────────────────────┘
</div>
</section>
<hr class="section-divider">
<!-- Piped Contact & Catalog Footer -->
<footer class="footer-links">
<a href="index.html" class="footer-link">Spec Catalog</a>
<span class="pipe">|</span>
<a href="https://git.rooks.dev" class="footer-link">Git Forge</a>
<span class="pipe">|</span>
<a href="mailto:river@rooks.dev" class="footer-link">river@rooks.dev</a>
<span class="pipe">|</span>
<span style="font-size:0.78rem; color:var(--text-muted);">CLUSTER ONLINE • 48TB ZFS POOL • 10GbE SFP+ BACKBONE</span>
</footer>
</main>
</div>
<!-- Pure Vanilla JS Filter Logic -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const filterBtns = document.querySelectorAll('#filterBar .filter-btn');
const serviceCards = document.querySelectorAll('#servicesGrid .service-card');
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
// Remove active class from all buttons
filterBtns.forEach(b => {
b.classList.remove('active');
b.setAttribute('aria-selected', 'false');
});
// Add active class to clicked button
btn.classList.add('active');
btn.setAttribute('aria-selected', 'true');
const filter = btn.getAttribute('data-filter');
// Toggle service cards visibility based on category
serviceCards.forEach(card => {
const category = card.getAttribute('data-category');
if (filter === 'all' || category === filter) {
card.style.display = 'flex';
} else {
card.style.display = 'none';
}
});
});
});
});
</script>
</body>
</html>
-497
View File
@@ -1,497 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Terminal/Minimalist Blog - Home</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
/* CSS Variables & Core Layout */
:root {
--bg-canvas: #121417;
--bg-surface: #1c1f26;
--border-color: #2b303b;
--text-primary: #e0e6f0;
--text-muted: #8a95a5;
--accent: #ffb000; /* Amber */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.6;
display: flex;
justify-content: center;
}
/* Rigid Grid System & App Wrapper */
.app-container {
display: grid;
grid-template-columns: 260px 1fr;
width: 100%;
max-width: 1200px;
min-height: 100vh;
border-left: 1px solid var(--border-color);
border-right: 1px solid var(--border-color);
}
@media (max-width: 900px) {
.app-container {
grid-template-columns: 1fr;
border-left: none;
border-right: none;
}
.sidebar {
border-right: none !important;
border-bottom: 1px solid var(--border-color);
}
}
/* Sidebar Navigation (Monospaced UI) */
.sidebar {
background-color: var(--bg-canvas);
border-right: 1px solid var(--border-color);
padding: 24px;
display: flex;
flex-direction: column;
font-family: 'IBM Plex Mono', monospace;
}
.profile-title {
font-size: 1.1rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 8px;
}
.profile-subtitle {
font-size: 0.85rem;
color: var(--text-muted);
margin-bottom: 24px;
}
.nav-list {
list-style: none;
padding: 0;
margin-bottom: auto;
}
.nav-item {
margin-bottom: 8px;
}
.nav-link {
display: flex;
align-items: center;
color: var(--text-primary);
text-decoration: none;
font-size: 0.9rem;
padding: 6px 8px;
border: 1px solid transparent;
}
/* Snap instant hovers */
.nav-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.nav-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
/* Structural Indicator for Active Nav Items */
.nav-link.active::before {
content: "> ";
color: var(--accent);
margin-right: 6px;
}
.nav-link.active:hover::before {
color: var(--bg-canvas);
}
/* Keybindings Panel */
.keybindings-panel {
border: 1px solid var(--border-color);
background-color: var(--bg-surface);
padding: 16px;
border-radius: 4px;
margin-top: 24px;
}
.keybindings-title {
font-size: 0.8rem;
font-weight: 500;
color: var(--text-primary);
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.keybinding-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.8rem;
margin-bottom: 8px;
}
.keybinding-row:last-child {
margin-bottom: 0;
}
kbd {
background-color: var(--bg-canvas);
border: 1px solid var(--border-color);
border-bottom: 1.5px solid var(--text-muted);
border-radius: 3px;
padding: 1px 5px;
font-size: 0.75rem;
color: var(--text-primary);
font-family: 'JetBrains Mono', monospace;
}
/* Main Content Area */
.main-content {
padding: 40px;
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
}
.prose-container {
width: 100%;
max-width: 65ch; /* Optimal readability cap */
}
/* Typography & Headers */
h1, h2, h3 {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
color: var(--text-primary);
margin-top: 0;
}
h1 {
font-size: 1.8rem;
margin-bottom: 16px;
}
h2 {
font-size: 1.3rem;
margin-top: 32px;
margin-bottom: 16px;
border-bottom: 1px solid var(--border-color);
padding-bottom: 8px;
}
p {
margin-bottom: 20px;
color: var(--text-primary);
}
a {
color: var(--accent);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Dividers */
hr {
border: 0;
border-top: 1px solid var(--border-color);
margin: 32px 0;
width: 100%;
}
/* Metadata Tags */
.post-metadata {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 24px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
align-items: center;
}
.tag {
color: var(--accent);
background-color: transparent;
padding: 0;
font-size: 0.85rem;
text-decoration: none;
}
.tag:hover {
text-decoration: underline;
}
/* Terminal-Style Code Block Component */
.code-block {
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
margin: 24px 0;
font-family: 'JetBrains Mono', monospace;
overflow: hidden;
}
.code-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.8rem;
color: var(--text-muted);
}
.code-filename {
color: var(--text-primary);
font-weight: 500;
}
.code-content {
padding: 16px;
overflow-x: auto;
font-size: 0.85rem;
line-height: 1.5;
}
.code-content pre {
margin: 0;
}
/* Simple Monospace Syntax Colors */
.syn-comment { color: var(--text-muted); }
.syn-keyword { color: #ff5555; }
.syn-string { color: #50fa7b; }
.syn-type { color: #8be9fd; }
.syn-var { color: #ffb86c; }
/* Dense Archive / Tabular Posts List */
.post-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.9rem;
}
.post-table th, .post-table td {
text-align: left;
padding: 10px 12px;
border-bottom: 1px solid var(--border-color);
}
.post-table th {
color: var(--text-muted);
font-weight: 500;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.post-table tbody tr {
cursor: pointer;
}
/* Tabular Snap Hover - Instantly inverts row */
.post-table tbody tr:hover td {
background-color: var(--accent);
color: var(--bg-canvas);
}
.post-table tbody tr:hover td a {
color: var(--bg-canvas);
}
.post-table td a {
color: var(--text-primary);
font-weight: 500;
}
.post-table .post-date {
color: var(--text-muted);
width: 110px;
}
.post-table tbody tr:hover .post-date {
color: var(--bg-canvas);
}
/* Footer */
footer {
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border-color);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
</style>
</head>
<body>
<div class="app-container">
<!-- Sidebar Navigation -->
<aside class="sidebar">
<div class="profile-title">River Rooks</div>
<div class="profile-subtitle">dev-blog@system:~$</div>
<nav>
<ul class="nav-list">
<li class="nav-item"><a href="index.html" class="nav-link">Catalog</a></li>
<li class="nav-item"><a href="homepage.html" class="nav-link active">Sample 1 (Home)</a></li>
<li class="nav-item"><a href="split-panel.html" class="nav-link">Sample 2 (Split)</a></li>
<li class="nav-item"><a href="companion.html" class="nav-link">Sample 3 (Daemon)</a></li>
<li class="nav-item"><a href="document-flow.html" class="nav-link">Sample 4 (Doc Flow)</a></li>
<li class="nav-item"><a href="portfolio.html" class="nav-link">Sample 5 (Portfolio)</a></li>
<li class="nav-item"><a href="homelab.html" class="nav-link">Sample 6 (Homelab)</a></li>
<li class="nav-item"><a href="public-services.html" class="nav-link">Sample 7 (Services)</a></li>
<li class="nav-item"><a href="sample-dashboard.html" class="nav-link">Sample 8 (Dashboard)</a></li>
<li class="nav-item"><a href="sample-command-center.html" class="nav-link">Sample 9 (Command)</a></li>
<li class="nav-item"><a href="sample-dual-pane-explorer.html" class="nav-link">Sample 10 (Explorer)</a></li>
</ul>
</nav>
<!-- Keybindings Panel Widget -->
<div class="keybindings-panel">
<div class="keybindings-title">Active Stack</div>
<div style="font-size: 0.75rem; color: var(--text-muted); line-height: 1.6;">
[#network-forensics]<br>
[#docker-traefik]<br>
[#arch-linux] [#neovim]<br>
[#wireguard] [#posix-sh]
</div>
</div>
</aside>
<!-- Main Content -->
<main class="main-content">
<div class="prose-container">
<!-- Post Title & Metadata Header -->
<article>
<h1>Reverse Engineering IoT Firmware: SPI Flash Extraction</h1>
<div class="post-metadata">
<span>Posted: 2026-07-20</span>
<span>•</span>
<span>Reading: 8 mins</span>
<span>•</span>
<a href="#" class="tag">[#firmware]</a>
<a href="#" class="tag">[#hardware]</a>
<a href="#" class="tag">[#security]</a>
<a href="#" class="tag">[#bus-pirate]</a>
</div>
<p>
In security audits of embedded systems, retrieving the firmware is usually the critical first step. When over-the-air updates are encrypted and the debug ports (UART, JTAG) are disabled in production silicon, dumping the physical SPI Flash memory chip becomes necessary.
</p>
<p>
In this write-up, we extract firmware directly from an 8-pin SOP SPI flash chip using a Bus Pirate. This hardware-level approach bypasses any bootloader lockdowns and extracts the raw partition table directly from the silicon.
</p>
<!-- Component 1: Terminal-Style Code Block with syntax styling -->
<div class="code-block">
<div class="code-header">
<span class="code-filename">extract_firmware.sh</span>
<span>Bash</span>
</div>
<div class="code-content">
<pre><code><span class="syn-comment">#!/bin/sh</span>
<span class="syn-comment"># Query the SPI flash details using flashrom via Bus Pirate</span>
flashrom -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=1M -r firmware_dump.bin
<span class="syn-comment"># Verify the integrity of the binary dump by comparing hashes</span>
<span class="syn-keyword">if</span> [ <span class="syn-var">$?</span> -eq 0 ]; <span class="syn-keyword">then</span>
echo <span class="syn-string">"Dump successful. Running md5sum..."</span>
md5sum firmware_dump.bin
<span class="syn-keyword">else</span>
echo <span class="syn-string">"Failed to read flash memory."</span> &gt;&amp;2
<span class="syn-keyword">exit</span> 1
<span class="syn-keyword">fi</span></code></pre>
</div>
</div>
<p>
After standard verification, we can proceed to unpack the bin file using tools like <code>binwalk</code>. If the partition table contains standard filesystem formats (e.g. SquashFS, jffs2), binwalk extracts it automatically. Otherwise, manual offset carving is required.
</p>
</article>
<hr>
<!-- Tabular Archive Index Section -->
<section>
<h2>Recent Writes &amp; Archive</h2>
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 20px;">
Dense, terminal-style index of prior posts. Press <kbd>Tab</kbd> to focus and navigate rows instantly.
</p>
<table class="post-table">
<thead>
<tr>
<th>Date</th>
<th>Title</th>
<th>Tags</th>
</tr>
</thead>
<tbody>
<tr onclick="location.href='#'">
<td class="post-date">2026-07-15</td>
<td><a href="#">Building a High-Availability Traefik Router with Docker Compose</a></td>
<td><span style="color: var(--accent);">[#homelab]</span></td>
</tr>
<tr onclick="location.href='#'">
<td class="post-date">2026-07-10</td>
<td><a href="#">POSIX sh vs. Bash: Portability in Modern Scripts</a></td>
<td><span style="color: var(--accent);">[#posix-sh]</span></td>
</tr>
<tr onclick="location.href='#'">
<td class="post-date">2026-06-28</td>
<td><a href="#">CTF Write-up: Heap Exploitation in glibc 2.35</a></td>
<td><span style="color: var(--accent);">[#ctf]</span></td>
</tr>
<tr onclick="location.href='#'">
<td class="post-date">2026-06-14</td>
<td><a href="#">Zero-Trust Network Isolation with WireGuard &amp; OPNsense VLANs</a></td>
<td><span style="color: var(--accent);">[#networking]</span></td>
</tr>
</tbody>
</table>
</section>
<!-- Footer -->
<footer>
<span>© 2026 River Rooks</span>
<span>Aligned with Canonical-Sample-Content.md</span>
</footer>
</div>
</main>
</div>
</body>
</html>
-787
View File
@@ -1,787 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Design Spec Samples Framework</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&family=Inter:wght@400;500;700&family=JetBrains+Mono:wght@400;500;700&display=swap');
:root {
--bg-canvas: #0d0f12;
--bg-surface: #121417;
--bg-elevated: #1c1f26;
--border-color: #2b303b;
--text-primary: #e0e6f0;
--text-muted: #8a95a5;
--accent-cyan: #00f0ff;
--accent-green: #00e676;
--accent-amber: #ffb700;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
/* Top Master Navigation Bar */
.master-header {
background-color: var(--bg-surface);
border-bottom: 1px solid var(--border-color);
padding: 10px 20px;
display: flex;
flex-direction: column;
gap: 10px;
position: sticky;
top: 0;
z-index: 1000;
}
.header-top-row {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.brand-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.05rem;
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
}
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent-cyan);
display: inline-block;
}
.header-tools {
display: flex;
align-items: center;
gap: 12px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
}
/* Samples Selector Pill Bar */
.samples-nav-bar {
display: flex;
align-items: center;
gap: 6px;
overflow-x: auto;
padding-bottom: 4px;
scrollbar-width: thin;
scrollbar-color: var(--border-color) transparent;
}
.sample-btn {
background-color: var(--bg-canvas);
color: var(--text-muted);
border: 1px solid var(--border-color);
padding: 6px 12px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
cursor: pointer;
border-radius: 3px;
white-space: nowrap;
transition: none; /* Instant snap */
display: inline-flex;
align-items: center;
gap: 6px;
text-decoration: none;
}
.sample-btn:hover {
color: var(--text-primary);
border-color: var(--accent-cyan);
background-color: var(--bg-elevated);
}
.sample-btn.active {
background-color: var(--accent-cyan);
color: var(--bg-canvas);
border-color: var(--accent-cyan);
font-weight: 700;
}
.btn-badge {
font-size: 0.68rem;
opacity: 0.85;
}
/* Sub-bar / Viewport Controls */
.viewport-bar {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 6px 20px;
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'JetBrains Mono', monospace;
font-size: 0.78rem;
color: var(--text-muted);
flex-wrap: wrap;
gap: 10px;
}
.current-url-display {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-primary);
}
.prompt-tag {
color: var(--accent-cyan);
font-weight: 700;
}
.frame-controls {
display: flex;
align-items: center;
gap: 8px;
}
.ctrl-btn {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
color: var(--text-muted);
padding: 3px 8px;
font-family: inherit;
font-size: 0.75rem;
cursor: pointer;
border-radius: 2px;
transition: none;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 4px;
}
.ctrl-btn:hover {
color: var(--text-primary);
border-color: var(--accent-cyan);
}
.ctrl-btn.active {
color: var(--accent-cyan);
border-color: var(--accent-cyan);
background-color: var(--bg-elevated);
}
/* Main Workspace / Viewport */
.workspace-wrapper {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
background-color: var(--bg-canvas);
}
.frame-container {
width: 100%;
max-width: 100%;
height: calc(100vh - 165px);
min-height: 560px;
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
display: flex;
flex-direction: column;
overflow: hidden;
transition: max-width 0.15s ease-in-out;
}
.frame-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 6px 14px;
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
}
.window-dots {
display: flex;
gap: 6px;
}
.window-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--border-color);
}
.preview-iframe {
width: 100%;
height: 100%;
border: none;
background-color: #0d0f12;
flex: 1;
}
/* Markdown Reader Mode Fallback Container */
.markdown-viewer {
display: none;
padding: 32px 24px;
overflow-y: auto;
height: 100%;
background-color: var(--bg-surface);
font-family: 'Inter', sans-serif;
}
.markdown-prose {
max-width: 75ch;
margin: 0 auto;
color: var(--text-primary);
}
.markdown-prose h1, .markdown-prose h2, .markdown-prose h3 {
font-family: 'IBM Plex Mono', monospace;
color: var(--text-primary);
margin: 24px 0 12px 0;
}
.markdown-prose pre {
background-color: var(--bg-canvas);
border: 1px solid var(--border-color);
padding: 14px;
border-radius: 4px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
overflow-x: auto;
margin: 16px 0;
}
.markdown-prose code {
font-family: 'JetBrains Mono', monospace;
color: var(--accent-cyan);
}
.markdown-prose table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
}
.markdown-prose th, .markdown-prose td {
border: 1px solid var(--border-color);
padding: 8px 12px;
text-align: left;
}
.markdown-prose th {
background-color: var(--bg-canvas);
}
/* Spec Catalog Grid Drawer */
.catalog-drawer {
display: none;
padding: 24px 16px 40px 16px;
max-width: 1100px;
margin: 0 auto;
width: 100%;
}
.drawer-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 16px;
margin-top: 16px;
}
.card {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 16px;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.card:hover {
border-color: var(--accent-cyan);
background-color: var(--bg-elevated);
}
.card-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.95rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 6px;
}
.card:hover .card-title {
color: var(--accent-cyan);
}
.card-desc {
font-size: 0.85rem;
color: var(--text-muted);
margin-bottom: 12px;
}
.card-footer {
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
border-top: 1px solid var(--border-color);
padding-top: 8px;
display: flex;
justify-content: space-between;
}
/* Bottom Hotkey Help Footer */
.master-footer {
background-color: var(--bg-surface);
border-top: 1px solid var(--border-color);
padding: 8px 20px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
kbd {
background-color: var(--bg-canvas);
border: 1px solid var(--border-color);
border-bottom: 1.5px solid var(--text-muted);
border-radius: 3px;
padding: 1px 4px;
font-size: 0.72rem;
color: var(--text-primary);
}
</style>
</head>
<body>
<!-- Top Master Navigation Bar with Full Sample Options -->
<header class="master-header">
<div class="header-top-row">
<a href="index.html" class="brand-title">
<span class="status-dot"></span>
<span>RIVER ROOKS // SAMPLES VIEWER</span>
</a>
<div class="header-tools">
<span>Hotkeys: <kbd>1</kbd>-<kbd>9</kbd>, <kbd>0</kbd></span>
<button class="ctrl-btn" id="btn-toggle-mode" onclick="toggleViewMode()">View: Frame Preview</button>
<a href="../index.html" class="ctrl-btn">Root Lander ↗</a>
</div>
</div>
<!-- Options Bar to Select Each Sample -->
<nav class="samples-nav-bar" id="sampleOptionsBar" aria-label="Sample Selection Options">
<button class="sample-btn active" data-file="homepage.html" onclick="loadSample('homepage.html', this)">
<span>[1] Homepage</span>
</button>
<button class="sample-btn" data-file="split-panel.html" onclick="loadSample('split-panel.html', this)">
<span>[2] Split Panel</span>
</button>
<button class="sample-btn" data-file="companion.html" onclick="loadSample('companion.html', this)">
<span>[3] Companion</span>
</button>
<button class="sample-btn" data-file="document-flow.html" onclick="loadSample('document-flow.html', this)">
<span>[4] Doc Flow</span>
</button>
<button class="sample-btn" data-file="portfolio.html" onclick="loadSample('portfolio.html', this)">
<span>[5] Portfolio</span>
</button>
<button class="sample-btn" data-file="homelab.html" onclick="loadSample('homelab.html', this)">
<span>[6] Homelab</span>
</button>
<button class="sample-btn" data-file="public-services.html" onclick="loadSample('public-services.html', this)">
<span>[7] Services</span>
</button>
<button class="sample-btn" data-file="sample-dashboard.html" onclick="loadSample('sample-dashboard.html', this)">
<span>[8] Dashboard</span>
</button>
<button class="sample-btn" data-file="sample-command-center.html" onclick="loadSample('sample-command-center.html', this)">
<span>[9] Command</span>
</button>
<button class="sample-btn" data-file="sample-dual-pane-explorer.html" onclick="loadSample('sample-dual-pane-explorer.html', this)">
<span>[0] Explorer</span>
</button>
<button class="sample-btn" data-file="canonical-sample-content.md" onclick="loadSample('canonical-sample-content.md', this)">
<span>[Dataset] Content Spec</span>
</button>
<button class="sample-btn" data-file="unified-design-specification.md" onclick="loadSample('unified-design-specification.md', this)">
<span>[Spec] Master Brief</span>
</button>
</nav>
</header>
<!-- Viewport & Iframe Tool Control Bar -->
<div class="viewport-bar">
<div class="current-url-display">
<span class="prompt-tag">$ agy --view=</span>
<span id="activeFilePath">homepage.html</span>
<span id="activeFileDesc" style="color: var(--text-muted); font-size: 0.72rem; margin-left: 6px;">[Sample 1: Developer Homepage]</span>
</div>
<div class="frame-controls">
<!-- Responsive Width Selector -->
<span style="color: var(--text-muted); margin-right: 4px;">Viewport:</span>
<button class="ctrl-btn active" id="vp-full" onclick="setViewport('100%', this)">100%</button>
<button class="ctrl-btn" id="vp-tablet" onclick="setViewport('768px', this)">768px</button>
<button class="ctrl-btn" id="vp-mobile" onclick="setViewport('380px', this)">380px</button>
<span style="color: var(--border-color);">|</span>
<!-- Direct Link Actions -->
<button class="ctrl-btn" onclick="reloadIframe()" title="Reload current sample">⟳ Reload</button>
<a href="homepage.html" id="openTabBtn" target="_blank" class="ctrl-btn" title="Open directly in new tab">Open Tab ↗</a>
</div>
</div>
<!-- Workspace: Live Iframe Container -->
<main class="workspace-wrapper" id="frameWrapper">
<div class="frame-container" id="frameContainer">
<div class="frame-header">
<div class="window-dots">
<span class="window-dot"></span>
<span class="window-dot"></span>
<span class="window-dot"></span>
</div>
<div id="frameStatusText">SANDBOX IFRAME READY • ISOLATED WORKSPACE</div>
<div>STDOUT [200 OK]</div>
</div>
<!-- Live Interactive Iframe -->
<iframe src="homepage.html" id="sampleIframe" class="preview-iframe" title="Sample Live Viewport"></iframe>
<!-- Markdown Viewer Fallback (for .md files) -->
<div id="markdownViewer" class="markdown-viewer">
<div class="markdown-prose" id="markdownContent"></div>
</div>
</div>
</main>
<!-- Spec Catalog Grid Drawer (Alternate View Mode) -->
<section class="catalog-drawer" id="catalogDrawer" aria-label="Catalog Grid View">
<div style="display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 12px;">
<h2 style="font-family: 'IBM Plex Mono', monospace; font-size: 1.2rem; color: var(--text-primary);">Design Spec Samples Catalog</h2>
<span style="font-family: 'IBM Plex Mono', monospace; font-size: 0.8rem; color: var(--text-muted);">15 Artifacts Registered</span>
</div>
<div class="drawer-grid" id="drawerGrid"></div>
</section>
<!-- Bottom Global Status Footer -->
<footer class="master-footer">
<div>Unified Design Language • Dark Slate Native (#0d0f12)</div>
<div>Press <kbd>1</kbd>-<kbd>0</kbd> to quick-switch • Canonical Dataset Compliant</div>
</footer>
<script>
const catalogData = [
{
key: '1',
file: 'homepage.html',
title: 'Sample 1: Developer Homepage',
type: 'html',
desc: 'Dark-mode developer homepage with sidebar navigation, active stack cluster, featured firmware SPI extraction write-up, and dense archive table.'
},
{
key: '2',
file: 'split-panel.html',
title: 'Sample 2: Split Panel Layout',
type: 'html',
desc: 'Fixed left terminal mascot with binary animation frames, and scrollable right panel showcasing the 5 canonical system modules.'
},
{
key: '3',
file: 'companion.html',
title: 'Sample 3: Companion Layout',
type: 'html',
desc: '50/50 vertical split layout with system module cards on the left and a reactive ASCII companion daemon (sys-daemon) on the right.'
},
{
key: '4',
file: 'document-flow.html',
title: 'Sample 4: Continuous Document Flow',
type: 'html',
desc: 'Continuous reading stream across system modules with pinned IntersectionObserver scroll-tracker daemon observing scroll depth.'
},
{
key: '5',
file: 'portfolio.html',
title: 'Sample 5: River Rooks Portfolio',
type: 'html',
desc: 'Single-column digital business card landing page with stdout terminal status block, 11-item active stack tags, and cryptographic links.'
},
{
key: '6',
file: 'homelab.html',
title: 'Sample 6: Homelab Lander',
type: 'html',
desc: 'Infrastructure catalog with category filter tabs, 12 microservices, 3 bare-metal server specs, and ASCII network topology routing map.'
},
{
key: '7',
file: 'public-services.html',
title: 'Sample 7: Public Services Portal',
type: 'html',
desc: 'Launcher portal for self-hosted web applications with real-time keyword filter, category pills, access levels, and PGP/SSH identity verification box.'
},
{
key: '8',
file: 'sample-dashboard.html',
title: 'Sample 8: 3-Tile Workspace Dashboard',
type: 'html',
desc: 'Neovim/Tmux 3-tile modal workspace featuring live services in left tile, River Rooks resume/bio in center, and 5-post blog archive in right tile.'
},
{
key: '9',
file: 'sample-command-center.html',
title: 'Sample 9: Tabbed Command Center',
type: 'html',
desc: 'Zed Editor / Ghostty tabbed utility layout with keyboard shortcuts [1], [2], [3] switching between Services, Blog Archive, and Resume Config.'
},
{
key: '0',
file: 'sample-dual-pane-explorer.html',
title: 'Sample 10: Dual-Pane Helix Tree Explorer',
type: 'html',
desc: 'Split-screen file-tree explorer with interactive document viewer and sys-daemon mascot reacting to folder item selection.'
},
{
key: 'd',
file: 'canonical-sample-content.md',
title: 'Canonical Content Dataset (Single Source of Truth)',
type: 'md',
desc: 'Master markdown dataset specifying canonical persona profile, services catalog, blog articles, engineering modules, and telemetry nodes.'
},
{
key: 'u',
file: 'unified-design-specification.md',
title: 'Unified Design Specification (Master Brief)',
type: 'md',
desc: 'Master specification uniting design language across homelab, technical notes, and portfolio.'
},
{
key: 's',
file: 'design-specification.md',
title: 'Design Specification (Primary Technical Brief)',
type: 'md',
desc: 'Primary specification document detailing technical web architecture guidelines, typography rules, color tokens, and anti-patterns.'
},
{
key: 'p',
file: 'portfolio-spec.md',
title: 'Portfolio Specification (Landing Page Brief)',
type: 'md',
desc: 'Specification sheet for the single-page under-construction portfolio card layout.'
},
{
key: 'c',
file: 'sample3-spec-sheet.md',
title: 'Sample 3 Specification (Companion Layout Brief)',
type: 'md',
desc: 'Specification sheet detailing the split-screen ASCII companion interaction suppression policy.'
}
];
let currentFile = 'homepage.html';
let currentMode = 'frame'; // 'frame' or 'grid'
function loadSample(fileName, btnElement) {
currentFile = fileName;
// Update active state in top options bar
document.querySelectorAll('#sampleOptionsBar .sample-btn').forEach(btn => {
btn.classList.remove('active');
if (btn.getAttribute('data-file') === fileName) {
btn.classList.add('active');
}
});
// Update active file label and direct open link
document.getElementById('activeFilePath').innerText = fileName;
document.getElementById('openTabBtn').href = fileName;
const itemInfo = catalogData.find(item => item.file === fileName);
if (itemInfo) {
document.getElementById('activeFileDesc').innerText = `[${itemInfo.title}]`;
}
const iframe = document.getElementById('sampleIframe');
const mdViewer = document.getElementById('markdownViewer');
if (fileName.endsWith('.html')) {
mdViewer.style.display = 'none';
iframe.style.display = 'block';
iframe.src = fileName;
} else {
// Render Markdown file via simple fetch & format
iframe.style.display = 'none';
mdViewer.style.display = 'block';
renderMarkdownFile(fileName);
}
// If currently in grid mode, switch back to frame preview mode
if (currentMode === 'grid') {
toggleViewMode();
}
}
async function renderMarkdownFile(fileName) {
const mdContent = document.getElementById('markdownContent');
mdContent.innerHTML = `<div style="font-family: monospace; color: var(--text-muted);">Fetching and parsing ${fileName}...</div>`;
try {
const response = await fetch(fileName);
if (!response.ok) throw new Error('File fetch failed');
const text = await response.text();
// Simple high-readability markdown format converter
let html = text
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
.replace(/^\> (.*$)/gim, '<blockquote>$1</blockquote>')
.replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
.replace(/\*(.*)\*/gim, '<em>$1</em>')
.replace(/```([\s\S]*?)```/gim, '<pre><code>$1</code></pre>')
.replace(/`([^`]+)`/gim, '<code>$1</code>')
.replace(/\n\n/gim, '<br><br>');
mdContent.innerHTML = html;
} catch (e) {
mdContent.innerHTML = `<div style="color: var(--accent-amber); font-family: monospace;">Failed to load markdown directly in browser. <a href="${fileName}" target="_blank" style="color: var(--accent-cyan);">Click here to view raw file</a></div>`;
}
}
function setViewport(width, btnElement) {
const container = document.getElementById('frameContainer');
container.style.maxWidth = width;
document.querySelectorAll('.frame-controls .ctrl-btn').forEach(btn => {
if (btn.id && btn.id.startsWith('vp-')) {
btn.classList.remove('active');
}
});
if (btnElement) btnElement.classList.add('active');
}
function reloadIframe() {
const iframe = document.getElementById('sampleIframe');
if (iframe.style.display !== 'none') {
iframe.src = iframe.src;
} else {
renderMarkdownFile(currentFile);
}
}
function toggleViewMode() {
const frameWrapper = document.getElementById('frameWrapper');
const catalogDrawer = document.getElementById('catalogDrawer');
const modeBtn = document.getElementById('btn-toggle-mode');
if (currentMode === 'frame') {
currentMode = 'grid';
frameWrapper.style.display = 'none';
catalogDrawer.style.display = 'block';
modeBtn.innerText = 'View: Grid Catalog';
renderDrawerGrid();
} else {
currentMode = 'frame';
frameWrapper.style.display = 'flex';
catalogDrawer.style.display = 'none';
modeBtn.innerText = 'View: Frame Preview';
}
}
function renderDrawerGrid() {
const grid = document.getElementById('drawerGrid');
grid.innerHTML = '';
catalogData.forEach(item => {
const card = document.createElement('div');
card.className = 'card';
card.onclick = () => loadSample(item.file, null);
card.innerHTML = `
<div>
<div class="card-title">${item.title}</div>
<div class="card-desc">${item.desc}</div>
</div>
<div class="card-footer">
<span>${item.file}</span>
<span style="color: var(--accent-cyan);">[LOAD FRAME ↗]</span>
</div>
`;
grid.appendChild(card);
});
}
// Keyboard Shortcuts (1-9, 0)
document.addEventListener('keydown', (e) => {
// If user is not typing in an input
if (['input', 'textarea'].includes(document.activeElement.tagName.toLowerCase())) return;
const keyMap = {
'1': 'homepage.html',
'2': 'split-panel.html',
'3': 'companion.html',
'4': 'document-flow.html',
'5': 'portfolio.html',
'6': 'homelab.html',
'7': 'public-services.html',
'8': 'sample-dashboard.html',
'9': 'sample-command-center.html',
'0': 'sample-dual-pane-explorer.html'
};
if (keyMap[e.key]) {
loadSample(keyMap[e.key], null);
}
});
// Check URL parameters on initialization
document.addEventListener('DOMContentLoaded', () => {
const params = new URLSearchParams(window.location.search);
const requestedSample = params.get('sample') || params.get('view');
if (requestedSample) {
const match = catalogData.find(item => item.file === requestedSample || item.key === requestedSample);
if (match) {
loadSample(match.file, null);
return;
}
}
// Default load Sample 1
loadSample('homepage.html', null);
});
</script>
</body>
</html>
-49
View File
@@ -1,49 +0,0 @@
# Project Brief: Temporary Single-Page Portfolio (Landing Page)
**Project Type:** Single-Page "Under Construction" / Digital Business Card
**Design Philosophy:** High-signal, low-noise. Developer-centric minimalism. (Matches primary site spec)
## 1. Executive Summary
This is a temporary, single-page landing site to serve as a digital presence while the full web architecture is being developed. It must adhere strictly to the global design rules established for the main site (no glassmorphism, no glows, monospace-heavy typography, dark charcoal palette). The layout should be a simple, single-column document flow.
## 2. Layout & Structure
The page will be a single, centered column with a maximum width of `65ch` (characters) to ensure optimal reading density.
### A. Header (Identity)
* **Main Title (H1):** "River Rooks"
* **Subtitle/Status:** A monospaced, muted status line (e.g., "SYSTEMS ARCHITECTURE & CYBERSECURITY") paired with a rigid, square, 8x8px solid active-state indicator dot (using the primary accent color).
### B. The "Under Construction" Terminal Block
* **Component:** A terminal-style code block.
* **Style:** 1px solid border, slightly lighter surface background.
* **Content:** Must present the "under construction" status as a terminal command and stdout output.
* *Example prompt:* `$ cat /var/log/status.txt`
* *Example output:* `Under construction. Full site infrastructure is currently being provisioned. Routing traffic to temporary landing page...`
### C. "Whoami" Section (Biography)
* **Header (H2):** Clean sans-serif or bold monospace.
* **Content:** Standard prose paragraph establishing background (e.g., studying cybersecurity at the University of Alabama College of Engineering, focus on network forensics and self-hosted infrastructure).
### D. Active Stack (Skills & Tech)
* **Component:** A dense cluster of inline metadata tags.
* **Style:** Small, muted, monospaced font. Bracketed format preferred (e.g., `[#network-forensics]`, `[#docker-traefik]`, `[#arch-linux]`, `[#neovim]`).
### E. Footer (Contact & Links)
* **Layout:** A single monospaced line separated by structural pipes (`|`).
* **Links:** GitHub, Email, PGP Key.
* **Interaction:** Links must have an immediate hover state (e.g., background color snap) with zero transition delay. No floaty animations.
## 3. Strict Typography & Color Application
* **Canvas:** Deep slate/charcoal (e.g., `#0d0f12`)
* **Primary Text:** Off-white/light ash (Sans-serif: Inter or Geist)
* **Metadata/Terminal Text:** Muted gray (Monospace: IBM Plex Mono or JetBrains Mono)
* **Accent (Designer's Choice):** A high-visibility terminal color for the status dot and hover states.
* **Dividers:** Use simple 1px solid horizontal rules to separate the Header, Main Content, and Footer.
-369
View File
@@ -1,369 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Systems Architecture & Cybersecurity</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
:root {
--bg-canvas: #0d0f12; /* Deep slate/charcoal */
--bg-surface: #121417; /* Slightly lighter surface background */
--border-color: #2b303b; /* Low-contrast 1px border */
--text-primary: #e0e6f0; /* Off-white prose */
--text-muted: #8a95a5; /* Monospaced metadata gray */
--accent: #00f0ff; /* High-visibility Cyan terminal accent */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.65;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Sticky Navigation Header */
.sticky-header {
position: sticky;
top: 0;
height: 56px;
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
z-index: 100;
font-family: 'IBM Plex Mono', monospace;
}
.logo-container {
font-weight: 700;
font-size: 0.95rem;
color: var(--text-primary);
}
.logo-prompt {
color: var(--accent);
}
.header-nav {
display: flex;
gap: 14px;
}
.nav-link {
color: var(--text-primary);
text-decoration: none;
font-size: 0.85rem;
padding: 4px 8px;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
transition: none;
}
.nav-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.nav-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.nav-link.active::before {
content: "> ";
color: var(--accent);
margin-right: 4px;
}
.nav-link.active:hover::before {
color: var(--bg-canvas);
}
/* Page Canvas Container */
.page-wrapper {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 60px 24px;
}
.content-container {
width: 100%;
max-width: 65ch; /* Capped reading width */
}
/* Header (Identity) */
.author-name {
font-family: 'IBM Plex Mono', monospace;
font-size: 2.2rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 8px;
letter-spacing: -0.02em;
}
.status-line {
display: flex;
align-items: center;
gap: 10px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
}
/* Rigid, square 8x8px active-state indicator dot */
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent);
display: inline-block;
flex-shrink: 0;
}
/* 1px Solid Dividers */
.section-divider {
border: 0;
border-top: 1px solid var(--border-color);
margin: 32px 0;
}
/* Under Construction Terminal Block */
.terminal-block {
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
font-family: 'JetBrains Mono', monospace;
overflow: hidden;
margin-bottom: 32px;
}
.terminal-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 8px 16px;
display: flex;
justify-content: space-between;
font-size: 0.78rem;
color: var(--text-muted);
}
.terminal-title {
color: var(--text-primary);
font-weight: 500;
}
.terminal-body {
padding: 16px;
font-size: 0.85rem;
line-height: 1.5;
}
.cmd-line {
color: var(--text-muted);
margin-bottom: 8px;
}
.prompt {
color: var(--accent);
font-weight: 700;
}
.cmd-output {
color: var(--text-primary);
}
.cursor-block {
display: inline-block;
width: 8px;
height: 15px;
background-color: var(--text-primary);
margin-left: 4px;
vertical-align: middle;
animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
to { visibility: hidden; }
}
/* Content Sections */
.section-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.1rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 12px;
}
.bio-section {
margin-bottom: 32px;
}
.bio-text {
font-size: 0.98rem;
color: var(--text-primary);
}
.stack-section {
margin-bottom: 32px;
}
.tag-cluster {
display: flex;
flex-wrap: wrap;
gap: 10px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
}
.tag {
color: var(--text-muted);
}
/* Piped Link Footer with Instant Color Snap */
.footer-links {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.footer-link {
color: var(--text-primary);
text-decoration: none;
padding: 2px 6px;
transition: none; /* Zero-delay snap */
}
.footer-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.footer-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.pipe {
color: var(--border-color);
}
</style>
</head>
<body>
<!-- Sticky Header -->
<header class="sticky-header">
<div class="logo-container">
<span class="logo-prompt">$</span> agy --page=portfolio
</div>
<nav class="header-nav">
<a href="index.html" class="nav-link">Catalog</a>
<a href="homepage.html" class="nav-link">Sample 1</a>
<a href="split-panel.html" class="nav-link">Sample 2</a>
<a href="companion.html" class="nav-link">Sample 3</a>
<a href="document-flow.html" class="nav-link">Sample 4</a>
<a href="portfolio.html" class="nav-link active">Sample 5</a>
<a href="homelab.html" class="nav-link">Sample 6</a>
<a href="public-services.html" class="nav-link">Sample 7</a>
<a href="sample-dashboard.html" class="nav-link">Sample 8</a>
<a href="sample-command-center.html" class="nav-link">Sample 9</a>
<a href="sample-dual-pane-explorer.html" class="nav-link">Sample 10</a>
</nav>
</header>
<!-- Page Content Area -->
<div class="page-wrapper">
<main class="content-container">
<!-- A. Header (Identity) -->
<header class="identity-header">
<h1 class="author-name">River Rooks</h1>
<div class="status-line">
<span class="status-dot"></span>
<span class="status-text">SYSTEMS ARCHITECTURE &amp; CYBERSECURITY</span>
</div>
</header>
<hr class="section-divider">
<!-- B. Under Construction Terminal Block -->
<section class="terminal-section" aria-label="Status Terminal">
<div class="terminal-block">
<div class="terminal-header">
<span class="terminal-title">/var/log/status.txt</span>
<span>STDOUT</span>
</div>
<div class="terminal-body">
<div class="cmd-line"><span class="prompt">$</span> cat /var/log/status.txt</div>
<div class="cmd-output">
Provisioning 10GbE homelab cluster. Routing traffic through Traefik v3 edge proxy...<span class="cursor-block"></span>
</div>
</div>
</div>
</section>
<!-- C. "Whoami" Section (Biography) -->
<section class="bio-section">
<h2 class="section-title">whoami</h2>
<p class="bio-text">
Cybersecurity student at the University of Alabama College of Engineering, focusing on network forensics, self-hosted infrastructure, systems architecture, and low-level Unix tooling.
</p>
</section>
<!-- D. Active Stack (Skills & Tech) -->
<section class="stack-section">
<h2 class="section-title">active_stack</h2>
<div class="tag-cluster">
<span class="tag">[#network-forensics]</span>
<span class="tag">[#docker-traefik]</span>
<span class="tag">[#arch-linux]</span>
<span class="tag">[#neovim]</span>
<span class="tag">[#wireguard]</span>
<span class="tag">[#posix-sh]</span>
<span class="tag">[#python]</span>
<span class="tag">[#golang]</span>
<span class="tag">[#rust]</span>
<span class="tag">[#proxmox]</span>
<span class="tag">[#truenas]</span>
</div>
</section>
<hr class="section-divider">
<!-- E. Footer (Contact & Links) -->
<footer class="footer-links">
<a href="https://git.rooks.dev" target="_blank" rel="noopener" class="footer-link">Git Forge</a>
<span class="pipe">|</span>
<a href="https://github.com" target="_blank" rel="noopener" class="footer-link">GitHub</a>
<span class="pipe">|</span>
<a href="mailto:river@rooks.dev" class="footer-link">river@rooks.dev</a>
<span class="pipe">|</span>
<a href="#" title="Fingerprint: 0x4A8E92F1C0B3D4E7" class="footer-link">PGP: 0x4A8E92F1...</a>
<span class="pipe">|</span>
<a href="index.html" class="footer-link">Spec Catalog</a>
</footer>
</main>
</div>
</body>
</html>
-902
View File
@@ -1,902 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Public Services & Applications Portal</title>
<meta name="description" content="Public portal for River Rooks self-hosted web applications, developer utilities, media services, and public identity endpoints.">
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
:root {
--bg-canvas: #0d0f12; /* Deep slate/charcoal */
--bg-surface: #121417; /* Solid surface container */
--bg-surface-alt: #181b21; /* Slightly lighter surface card */
--border-color: #2b303b; /* Low-contrast 1px border */
--border-active: #ffb000; /* Active Amber border */
--text-primary: #e0e6f0; /* Off-white prose */
--text-muted: #8a95a5; /* Monospaced metadata gray */
--accent: #ffb000; /* High-visibility Amber terminal accent */
--accent-cyan: #00f0ff; /* Cyan secondary accent */
--accent-green: #00ff66; /* Green status indicator */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.65;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Sticky Navigation Header */
.sticky-header {
position: sticky;
top: 0;
height: 56px;
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
z-index: 100;
font-family: 'IBM Plex Mono', monospace;
}
.logo-container {
font-weight: 700;
font-size: 0.92rem;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.logo-prompt {
color: var(--accent);
}
.header-nav {
display: flex;
gap: 10px;
overflow-x: auto;
}
.nav-link {
color: var(--text-primary);
text-decoration: none;
font-size: 0.82rem;
padding: 4px 8px;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
transition: none; /* Zero-delay snap */
white-space: nowrap;
}
.nav-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.nav-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.nav-link.active::before {
content: "> ";
color: var(--accent);
margin-right: 4px;
}
.nav-link.active:hover::before {
color: var(--bg-canvas);
}
/* Main Page Wrapper */
.page-wrapper {
flex: 1;
display: flex;
justify-content: center;
padding: 40px 24px 60px 24px;
}
.content-container {
width: 100%;
max-width: 1000px;
}
/* Hero Banner */
.hero-header {
margin-bottom: 28px;
}
.site-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 2.2rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 8px;
letter-spacing: -0.02em;
}
.site-subtitle {
font-size: 1rem;
color: var(--text-muted);
max-width: 70ch;
}
/* Dividers */
.section-divider {
border: 0;
border-top: 1px solid var(--border-color);
margin: 32px 0;
}
/* Search & Filter Controls */
.controls-panel {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 32px;
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 16px;
}
.search-box {
display: flex;
align-items: center;
background-color: var(--bg-canvas);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 8px 12px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.88rem;
}
.search-prompt {
color: var(--accent);
margin-right: 10px;
font-weight: 700;
}
.search-input {
background: none;
border: none;
color: var(--text-primary);
font-family: inherit;
font-size: inherit;
width: 100%;
outline: none;
}
.search-input::placeholder {
color: var(--text-muted);
}
.filter-bar {
display: flex;
flex-wrap: wrap;
gap: 8px;
font-family: 'IBM Plex Mono', monospace;
}
.filter-btn {
background-color: var(--bg-canvas);
color: var(--text-muted);
border: 1px solid var(--border-color);
padding: 6px 12px;
font-size: 0.8rem;
cursor: pointer;
font-family: inherit;
border-radius: 4px;
transition: none; /* Instant snap */
}
.filter-btn:hover {
color: var(--text-primary);
border-color: var(--accent);
}
.filter-btn.active {
background-color: var(--accent);
color: var(--bg-canvas);
border-color: var(--accent);
font-weight: 700;
}
.filter-btn:focus {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
/* Section Headers */
.section-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 16px;
flex-wrap: wrap;
gap: 8px;
}
.section-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.15rem;
font-weight: 700;
color: var(--text-primary);
}
.section-meta {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
}
/* Featured Services Grid */
.featured-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
gap: 16px;
margin-bottom: 36px;
}
.featured-card {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
}
.featured-card:hover {
border-color: var(--accent);
}
.featured-badge {
align-self: flex-start;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.72rem;
font-weight: 700;
color: var(--bg-canvas);
background-color: var(--accent);
padding: 2px 6px;
border-radius: 2px;
margin-bottom: 12px;
text-transform: uppercase;
}
.service-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.1rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 8px;
}
.featured-card:hover .service-title {
color: var(--accent);
}
.service-body {
font-size: 0.9rem;
color: var(--text-muted);
margin-bottom: 20px;
line-height: 1.5;
}
.action-row {
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.82rem;
padding-top: 12px;
border-top: 1px solid var(--border-color);
}
.access-tag {
color: var(--text-muted);
}
.launch-btn {
display: inline-flex;
align-items: center;
gap: 4px;
background-color: var(--bg-canvas);
color: var(--text-primary);
border: 1px solid var(--border-color);
padding: 6px 12px;
text-decoration: none;
font-weight: 500;
border-radius: 4px;
transition: none;
}
.launch-btn:hover {
background-color: var(--accent);
color: var(--bg-canvas);
border-color: var(--accent);
}
.launch-btn:focus {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
/* All Public Services Grid */
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
margin-bottom: 36px;
}
.service-card {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 18px;
display: flex;
flex-direction: column;
justify-content: space-between;
transition: none;
}
.service-card:hover {
border-color: var(--border-active);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 8px;
}
.card-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1rem;
font-weight: 700;
color: var(--text-primary);
}
.service-card:hover .card-title {
color: var(--accent);
}
.card-category {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
}
.card-desc {
font-size: 0.88rem;
color: var(--text-muted);
margin-bottom: 16px;
line-height: 1.5;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
padding-top: 10px;
border-top: 1px solid var(--border-color);
}
.badge-access {
display: inline-flex;
align-items: center;
gap: 6px;
}
.dot-public {
width: 6px;
height: 6px;
background-color: var(--accent-green);
display: inline-block;
}
.dot-auth {
width: 6px;
height: 6px;
background-color: var(--accent);
display: inline-block;
}
.direct-link {
color: var(--text-primary);
text-decoration: none;
padding: 3px 8px;
border: 1px solid var(--border-color);
border-radius: 3px;
background-color: var(--bg-canvas);
}
.direct-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
border-color: var(--accent);
}
/* Public Identity & Quick Keys Box */
.identity-box {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 20px;
font-family: 'JetBrains Mono', monospace;
margin-bottom: 36px;
}
.identity-title {
font-size: 1rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 14px;
}
.key-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid var(--border-color);
font-size: 0.83rem;
flex-wrap: wrap;
gap: 10px;
}
.key-row:last-child {
border-bottom: none;
padding-bottom: 0;
}
.key-label {
color: var(--text-muted);
}
.key-value {
color: var(--text-primary);
}
.copy-btn {
background-color: var(--bg-canvas);
color: var(--text-primary);
border: 1px solid var(--border-color);
padding: 4px 8px;
font-family: inherit;
font-size: 0.75rem;
cursor: pointer;
border-radius: 3px;
transition: none;
}
.copy-btn:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
/* Piped Footer Links */
.footer-links {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.footer-link {
color: var(--text-primary);
text-decoration: none;
padding: 2px 6px;
transition: none; /* Zero-delay snap */
}
.footer-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.footer-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
.pipe {
color: var(--border-color);
}
</style>
</head>
<body>
<!-- Sticky Header -->
<header class="sticky-header">
<div class="logo-container">
<span class="logo-prompt">$</span> agy --page=public-services
</div>
<nav class="header-nav" aria-label="Main Navigation">
<a href="index.html" class="nav-link">Catalog</a>
<a href="homepage.html" class="nav-link">Sample 1</a>
<a href="split-panel.html" class="nav-link">Sample 2</a>
<a href="companion.html" class="nav-link">Sample 3</a>
<a href="document-flow.html" class="nav-link">Sample 4</a>
<a href="portfolio.html" class="nav-link">Sample 5</a>
<a href="homelab.html" class="nav-link">Sample 6</a>
<a href="public-services.html" class="nav-link active">Sample 7 (Public Services)</a>
<a href="sample-dashboard.html" class="nav-link">Sample 8</a>
<a href="sample-command-center.html" class="nav-link">Sample 9</a>
<a href="sample-dual-pane-explorer.html" class="nav-link">Sample 10</a>
</nav>
</header>
<!-- Page Content Area -->
<div class="page-wrapper">
<main class="content-container">
<!-- Hero Header -->
<header class="hero-header">
<h1 class="site-title">Public Services Portal</h1>
<p class="site-subtitle">
Direct access to public self-hosted web applications, developer utilities, repository mirrors, and interactive tools operated by River Rooks.
</p>
</header>
<!-- Instant Search & Category Filter Controls -->
<section aria-label="Search and Filter Controls">
<div class="controls-panel">
<div class="search-box">
<span class="search-prompt">$ search_services</span>
<input type="text" id="searchInput" class="search-input" placeholder="type service name, tag, or technology..." aria-label="Search Public Services">
</div>
<div class="filter-bar" id="filterBar" role="tablist" aria-label="Service Categories">
<button class="filter-btn active" data-filter="all" role="tab" aria-selected="true">All Services</button>
<button class="filter-btn" data-filter="dev" role="tab">Developer Services</button>
<button class="filter-btn" data-filter="tools" role="tab">Utilities &amp; Tools</button>
<button class="filter-btn" data-filter="media" role="tab">Media &amp; Content</button>
<button class="filter-btn" data-filter="security" role="tab">Identity &amp; Security</button>
</div>
</div>
</section>
<!-- Featured Services Section -->
<section aria-label="Featured Public Services">
<div class="section-header">
<h2 class="section-title">featured_flagships</h2>
<span class="section-meta">Primary Public Endpoints</span>
</div>
<div class="featured-grid">
<!-- Forgejo -->
<article class="featured-card">
<div>
<span class="featured-badge">Open Source Git Forge</span>
<h3 class="service-title">Forgejo Repositories</h3>
<p class="service-body">
Self-hosted Git platform hosting public source code for network security scripts, Neovim dotfiles, Ansible playbooks, and Docker compose templates.
</p>
</div>
<div class="action-row">
<span class="access-tag">Access: Public Read</span>
<a href="#" class="launch-btn">Explore Repos ↗</a>
</div>
</article>
<!-- CyberChef -->
<article class="featured-card">
<div>
<span class="featured-badge">Security Utility</span>
<h3 class="service-title">CyberChef Workbench</h3>
<p class="service-body">
Web-based data transformation tool for hex decoding, Base64 operations, regex extraction, cryptographic hashing, and CTF payload analysis.
</p>
</div>
<div class="action-row">
<span class="access-tag">Access: Open Web</span>
<a href="#" class="launch-btn">Launch App ↗</a>
</div>
</article>
<!-- Stirling PDF -->
<article class="featured-card">
<div>
<span class="featured-badge">Document Tools</span>
<h3 class="service-title">Stirling PDF Studio</h3>
<p class="service-body">
Locally hosted document processing suite for merging, splitting, OCR extraction, password encryption, and PDF page manipulation.
</p>
</div>
<div class="action-row">
<span class="access-tag">Access: Open Web</span>
<a href="#" class="launch-btn">Launch App ↗</a>
</div>
</article>
</div>
</section>
<hr class="section-divider">
<!-- All Public Services Catalog Grid -->
<section aria-label="Complete Public Services Directory">
<div class="section-header">
<h2 class="section-title">services_catalog</h2>
<span class="section-meta" id="resultCount">Showing 9 Services</span>
</div>
<div class="services-grid" id="servicesGrid">
<!-- Forgejo Git -->
<article class="service-card" data-category="dev" data-search="forgejo git repo code source open">
<div>
<div class="card-header">
<h3 class="card-title">Forgejo Git Forge</h3>
<span class="card-category">[#dev-git]</span>
</div>
<p class="card-desc">Public repository mirror for software projects, dotfiles, and CTF writeups.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-public"></span> PUBLIC</span>
<a href="#" class="direct-link">git.rooks.dev ↗</a>
</div>
</article>
<!-- CyberChef -->
<article class="service-card" data-category="tools" data-search="cyberchef decode base64 crypto hex ctf">
<div>
<div class="card-header">
<h3 class="card-title">CyberChef Workbench</h3>
<span class="card-category">[#security-tool]</span>
</div>
<p class="card-desc">Cyber Swiss Army Knife for encoding, decoding, hashing, and data parsing.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-public"></span> PUBLIC</span>
<a href="#" class="direct-link">chef.rooks.dev ↗</a>
</div>
</article>
<!-- Stirling PDF -->
<article class="service-card" data-category="tools" data-search="pdf stirling merge split ocr document">
<div>
<div class="card-header">
<h3 class="card-title">Stirling PDF Studio</h3>
<span class="card-category">[#pdf-tools]</span>
</div>
<p class="card-desc">Complete PDF manipulation tool for merging, splitting, and OCR scanning.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-public"></span> PUBLIC</span>
<a href="#" class="direct-link">pdf.rooks.dev ↗</a>
</div>
</article>
<!-- Hedgedoc -->
<article class="service-card" data-category="dev" data-search="hedgedoc codimd markdown notes collab">
<div>
<div class="card-header">
<h3 class="card-title">HedgeDoc Collaborative</h3>
<span class="card-category">[#markdown]</span>
</div>
<p class="card-desc">Real-time collaborative Markdown editor for technical specs and meeting notes.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-public"></span> OPEN</span>
<a href="#" class="direct-link">pad.rooks.dev ↗</a>
</div>
</article>
<!-- Jellyfin Media -->
<article class="service-card" data-category="media" data-search="jellyfin video stream media movie tv">
<div>
<div class="card-header">
<h3 class="card-title">Jellyfin Media Portal</h3>
<span class="card-category">[#media-video]</span>
</div>
<p class="card-desc">Personal video library with 4K HDR hardware acceleration and mobile streaming.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-auth"></span> ACCOUNT REQ</span>
<a href="#" class="direct-link">watch.rooks.dev ↗</a>
</div>
</article>
<!-- Audiobookshelf -->
<article class="service-card" data-category="media" data-search="audiobookshelf audio book podcast library">
<div>
<div class="card-header">
<h3 class="card-title">Audiobookshelf</h3>
<span class="card-category">[#media-audio]</span>
</div>
<p class="card-desc">Self-hosted audiobook and podcast server with position syncing across devices.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-auth"></span> INVITE ONLY</span>
<a href="#" class="direct-link">listen.rooks.dev ↗</a>
</div>
</article>
<!-- Calibre-Web -->
<article class="service-card" data-category="media" data-search="calibre ebook pdf book reading library">
<div>
<div class="card-header">
<h3 class="card-title">Calibre-Web Library</h3>
<span class="card-category">[#ebooks]</span>
</div>
<p class="card-desc">E-book library catalog with e-reader OPDS feed and browser reading support.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-public"></span> PUBLIC READ</span>
<a href="#" class="direct-link">read.rooks.dev ↗</a>
</div>
</article>
<!-- Vaultwarden -->
<article class="service-card" data-category="security" data-search="vaultwarden bitwarden password vault secrets 2fa">
<div>
<div class="card-header">
<h3 class="card-title">Vaultwarden Password Vault</h3>
<span class="card-category">[#secrets]</span>
</div>
<p class="card-desc">End-to-end encrypted password manager endpoint for authorized client syncing.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-auth"></span> 2FA / AUTH</span>
<a href="#" class="direct-link">vault.rooks.dev ↗</a>
</div>
</article>
<!-- Matrix Synapse -->
<article class="service-card" data-category="security" data-search="matrix chat element federation identity messaging">
<div>
<div class="card-header">
<h3 class="card-title">Matrix Federated Chat</h3>
<span class="card-category">[#messaging]</span>
</div>
<p class="card-desc">Federated Matrix homeserver for encrypted messaging and public discussion rooms.</p>
</div>
<div class="card-footer">
<span class="badge-access"><span class="dot-public"></span> FEDERATED</span>
<a href="#" class="direct-link">matrix.rooks.dev ↗</a>
</div>
</article>
</div>
</section>
<hr class="section-divider">
<!-- Public Identity & Key Verification Section -->
<section aria-label="Identity and Public Keys">
<div class="section-header">
<h2 class="section-title">public_keys_&amp;_verification</h2>
<span class="section-meta">Cryptographic Identity</span>
</div>
<div class="identity-box">
<div class="identity-title">// Public Endpoints &amp; Cryptographic Fingerprints</div>
<div class="key-row">
<div>
<span class="key-label">PGP Key ID / Fingerprint:</span><br>
<span class="key-value">0x4A8E92F1C0B3D4E7 • 3F8A B29C 7D1E 4056 9912 A8B0 C4D3 E2F1 8091 22A4</span>
</div>
<button class="copy-btn" onclick="navigator.clipboard.writeText('0x4A8E92F1C0B3D4E7')">Copy Key ID</button>
</div>
<div class="key-row">
<div>
<span class="key-label">Matrix Handle:</span><br>
<span class="key-value">@river:rooks.dev</span>
</div>
<button class="copy-btn" onclick="navigator.clipboard.writeText('@river:rooks.dev')">Copy Matrix ID</button>
</div>
<div class="key-row">
<div>
<span class="key-label">Public SSH Key Digest:</span><br>
<span class="key-value">SHA256:7uK+N04wP5rL/9z3Q8s1vX4dF6mK2pT8yB1wN0qZ8 (river@rooks.dev)</span>
</div>
<button class="copy-btn" onclick="navigator.clipboard.writeText('SHA256:7uK+N04wP5rL/9z3Q8s1vX4dF6mK2pT8yB1wN0qZ8')">Copy Digest</button>
</div>
</div>
</section>
<hr class="section-divider">
<!-- Piped Contact Footer -->
<footer class="footer-links">
<a href="index.html" class="footer-link">Spec Catalog</a>
<span class="pipe">|</span>
<a href="https://git.rooks.dev" class="footer-link">Git Forge</a>
<span class="pipe">|</span>
<a href="mailto:river@rooks.dev" class="footer-link">river@rooks.dev</a>
<span class="pipe">|</span>
<span style="font-size:0.78rem; color:var(--text-muted);">PUBLIC SERVICES PORTAL • RIVER ROOKS</span>
</footer>
</main>
</div>
<!-- Pure Vanilla JS Search & Filter Script -->
<script>
document.addEventListener('DOMContentLoaded', () => {
const searchInput = document.getElementById('searchInput');
const filterBtns = document.querySelectorAll('#filterBar .filter-btn');
const serviceCards = document.querySelectorAll('#servicesGrid .service-card');
const resultCount = document.getElementById('resultCount');
let currentFilter = 'all';
let currentQuery = '';
function updateResults() {
let visibleCount = 0;
serviceCards.forEach(card => {
const category = card.getAttribute('data-category');
const searchData = card.getAttribute('data-search').toLowerCase();
const cardText = card.innerText.toLowerCase();
const matchesCategory = (currentFilter === 'all' || category === currentFilter);
const matchesQuery = currentQuery === '' || searchData.includes(currentQuery) || cardText.includes(currentQuery);
if (matchesCategory && matchesQuery) {
card.style.display = 'flex';
visibleCount++;
} else {
card.style.display = 'none';
}
});
resultCount.textContent = `Showing ${visibleCount} Service${visibleCount !== 1 ? 's' : ''}`;
}
// Filter Buttons Click Listener
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => {
b.classList.remove('active');
b.setAttribute('aria-selected', 'false');
});
btn.classList.add('active');
btn.setAttribute('aria-selected', 'true');
currentFilter = btn.getAttribute('data-filter');
updateResults();
});
});
// Search Input Listener
searchInput.addEventListener('input', (e) => {
currentQuery = e.target.value.trim().toLowerCase();
updateResults();
});
});
</script>
</body>
</html>
-521
View File
@@ -1,521 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Command Center</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&family=Inter:wght@400;500;700&family=JetBrains+Mono:wght@400;500;700&display=swap');
:root {
--bg-canvas: #0d0f12;
--bg-surface: #121417;
--bg-elevated: #1c1f26;
--border-color: #2b303b;
--text-primary: #e0e6f0;
--text-muted: #8a95a5;
--accent-cyan: #00f0ff;
--accent-green: #00e676;
--accent-amber: #ffb700;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Command Center Top Navigation Header */
.app-header {
background-color: var(--bg-surface);
border-bottom: 1px solid var(--border-color);
padding: 0 16px;
display: flex;
justify-content: space-between;
align-items: center;
height: 48px;
}
.brand-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.95rem;
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 10px;
}
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent-cyan);
}
/* Zed-Style Editor Tabs */
.nav-tabs {
display: flex;
gap: 2px;
height: 100%;
align-items: flex-end;
}
.tab-btn {
background-color: var(--bg-canvas);
border: 1px solid var(--border-color);
border-bottom: none;
color: var(--text-muted);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.82rem;
padding: 8px 16px;
cursor: pointer;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
transition: none; /* Instant snap */
}
.tab-btn:hover {
color: var(--text-primary);
background-color: var(--bg-elevated);
}
.tab-btn.active {
background-color: var(--bg-surface);
color: var(--accent-cyan);
border-top: 2px solid var(--accent-cyan);
font-weight: 700;
}
/* Main Container */
.container {
max-width: 900px;
width: 100%;
margin: 32px auto;
padding: 0 20px;
flex: 1;
}
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
/* Panel Component */
.panel {
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-surface);
overflow: hidden;
margin-bottom: 24px;
}
.panel-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 10px 16px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
font-weight: 700;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
.panel-body {
padding: 20px;
}
/* Services Grid (Tab 1) */
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 16px;
}
.service-box {
border: 1px solid var(--border-color);
background-color: var(--bg-canvas);
padding: 14px;
border-radius: 2px;
}
.service-box:hover {
border-color: var(--accent-cyan);
}
.service-title {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
font-size: 0.92rem;
display: flex;
justify-content: space-between;
margin-bottom: 6px;
}
.service-title a {
color: var(--text-primary);
text-decoration: none;
transition: none;
}
.service-title a:hover {
color: var(--accent-cyan);
}
.badge-status {
font-size: 0.75rem;
padding: 2px 6px;
border-radius: 2px;
font-family: 'IBM Plex Mono', monospace;
}
.badge-online { background-color: rgba(0, 230, 118, 0.15); color: var(--accent-green); }
.badge-internal { background-color: rgba(255, 183, 0, 0.15); color: var(--accent-amber); }
.service-desc {
font-size: 0.85rem;
color: var(--text-muted);
margin-bottom: 12px;
}
.service-stats {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
border-top: 1px solid var(--border-color);
padding-top: 8px;
display: flex;
justify-content: space-between;
}
/* Blog Archive Table (Tab 2) */
.blog-table {
width: 100%;
border-collapse: collapse;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
}
.blog-table th {
text-align: left;
border-bottom: 1px solid var(--border-color);
padding: 10px;
color: var(--text-muted);
font-size: 0.78rem;
}
.blog-table td {
padding: 12px 10px;
border-bottom: 1px solid var(--border-color);
}
.blog-table tr:hover td {
background-color: var(--bg-canvas);
}
.post-link {
color: var(--text-primary);
text-decoration: none;
font-weight: 500;
transition: none;
}
.post-link:hover {
color: var(--accent-cyan);
}
.tag-badge {
color: var(--text-muted);
font-size: 0.78rem;
}
/* Resume Config Document (Tab 3) */
.conf-block {
background-color: var(--bg-canvas);
border: 1px solid var(--border-color);
padding: 16px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.85rem;
border-radius: 4px;
margin-bottom: 20px;
}
.conf-key { color: var(--accent-cyan); }
.conf-val { color: var(--text-primary); }
.conf-comment { color: var(--text-muted); }
.btn-action {
background-color: var(--bg-elevated);
border: 1px solid var(--border-color);
color: var(--text-primary);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
padding: 8px 16px;
cursor: pointer;
text-decoration: none;
display: inline-block;
transition: none;
}
.btn-action:hover {
background-color: var(--accent-cyan);
color: var(--bg-canvas);
}
/* Footer */
.cmd-footer {
border-top: 1px solid var(--border-color);
background-color: var(--bg-surface);
padding: 12px 20px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
</style>
</head>
<body>
<!-- Top App Bar & Zed-style Tabs -->
<header class="app-header">
<div class="brand-title">
<span class="status-dot"></span>
<a href="index.html" style="color: var(--text-primary); text-decoration: none;">RIVER ROOKS // COMMAND CENTER</a>
</div>
<nav class="nav-tabs">
<button class="tab-btn active" onclick="switchTab('services')">[1] ~/services</button>
<button class="tab-btn" onclick="switchTab('blog')">[2] ~/blog</button>
<button class="tab-btn" onclick="switchTab('resume')">[3] ~/resume</button>
</nav>
</header>
<!-- Main Container -->
<main class="container">
<!-- Tab 1: Homelab & Public Services Directory -->
<div id="tab-services" class="tab-pane active">
<div class="panel">
<div class="panel-header">
<span>/etc/services.d/manifest.json</span>
<span>6 CANONICAL SERVICES</span>
</div>
<div class="panel-body">
<div class="services-grid">
<div class="service-box">
<div class="service-title">
<a href="https://proxy.rooks.dev" target="_blank">Traefik Proxy (v3.1)</a>
<span class="badge-status badge-online">ONLINE</span>
</div>
<p class="service-desc">Edge HTTP reverse proxy with dynamic ACME Let's Encrypt TLS wildcard certificates.</p>
<div class="service-stats">
<span>PORT: 443/tcp</span>
<span>#core</span>
</div>
</div>
<div class="service-box">
<div class="service-title">
<a href="https://nas.rooks.dev" target="_blank">TrueNAS SCALE</a>
<span class="badge-status badge-online">ONLINE</span>
</div>
<p class="service-desc">Primary storage pool managing 48TB ZFS RaidZ2 array with daily scrub jobs.</p>
<div class="service-stats">
<span>NFS / SMB</span>
<span>#storage</span>
</div>
</div>
<div class="service-box">
<div class="service-title">
<a href="https://vault.rooks.dev" target="_blank">Vaultwarden Secrets</a>
<span class="badge-status badge-online">E2EE</span>
</div>
<p class="service-desc">Rust implementation of Bitwarden API hosting credentials, SSH keys, &amp; 2FA.</p>
<div class="service-stats">
<span>2FA / AUTH</span>
<span>#secrets</span>
</div>
</div>
<div class="service-box">
<div class="service-title">
<a href="https://git.rooks.dev" target="_blank">Forgejo Git Forge</a>
<span class="badge-status badge-online">PUBLIC</span>
</div>
<p class="service-desc">Self-hosted Git platform for dotfiles, playbooks, and CTF binary writeups.</p>
<div class="service-stats">
<span>PORT: 3000</span>
<span>#dev-git</span>
</div>
</div>
<div class="service-box">
<div class="service-title">
<a href="https://chef.rooks.dev" target="_blank">CyberChef Workbench</a>
<span class="badge-status badge-online">PUBLIC</span>
</div>
<p class="service-desc">Hex decoding, Base64 operations, regex extraction, &amp; payload analysis.</p>
<div class="service-stats">
<span>OPEN WEB</span>
<span>#security-tool</span>
</div>
</div>
<div class="service-box">
<div class="service-title">
<a href="https://pve.rooks.dev" target="_blank">Proxmox VE (v8.2)</a>
<span class="badge-status badge-online">ONLINE</span>
</div>
<p class="service-desc">Hypervisor cluster hosting Debian LXC containers &amp; KVM virtual machines.</p>
<div class="service-stats">
<span>PORT: 8006</span>
<span>#compute</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Tab 2: Technical Notes & Blog Archive -->
<div id="tab-blog" class="tab-pane">
<div class="panel">
<div class="panel-header">
<span>/var/log/articles.index</span>
<span>POST COUNT: 5</span>
</div>
<div class="panel-body" style="padding: 0;">
<table class="blog-table">
<thead>
<tr>
<th>DATE</th>
<th>TITLE</th>
<th>TAGS</th>
<th>READ TIME</th>
</tr>
</thead>
<tbody>
<tr>
<td>2026-07-20</td>
<td><a href="#" class="post-link">Reverse Engineering IoT Firmware: SPI Flash Extraction</a></td>
<td><span class="tag-badge">[#firmware] [#hardware]</span></td>
<td>8 min</td>
</tr>
<tr>
<td>2026-07-15</td>
<td><a href="#" class="post-link">Building a High-Availability Traefik Router with Docker Compose</a></td>
<td><span class="tag-badge">[#homelab] [#traefik]</span></td>
<td>12 min</td>
</tr>
<tr>
<td>2026-07-10</td>
<td><a href="#" class="post-link">POSIX sh vs. Bash: Portability in Modern Scripts</a></td>
<td><span class="tag-badge">[#posix-sh] [#unix]</span></td>
<td>6 min</td>
</tr>
<tr>
<td>2026-06-28</td>
<td><a href="#" class="post-link">CTF Write-up: Heap Exploitation in glibc 2.35</a></td>
<td><span class="tag-badge">[#ctf] [#binary]</span></td>
<td>15 min</td>
</tr>
<tr>
<td>2026-06-14</td>
<td><a href="#" class="post-link">Zero-Trust Network Isolation with WireGuard &amp; OPNsense VLANs</a></td>
<td><span class="tag-badge">[#networking] [#wireguard]</span></td>
<td>10 min</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Tab 3: Resume & Engineering Profile -->
<div id="tab-resume" class="tab-pane">
<div class="panel">
<div class="panel-header">
<span>/etc/river_rooks.conf</span>
<span>POSIX SPECIFICATION</span>
</div>
<div class="panel-body">
<div class="conf-block">
<span class="conf-comment"># Identity &amp; Systems Architecture</span><br>
<span class="conf-key">AUTHOR</span> = <span class="conf-val">"River Rooks"</span><br>
<span class="conf-key">ROLE</span> = <span class="conf-val">"Systems Architecture &amp; Cybersecurity"</span><br>
<span class="conf-key">AFFILIATION</span> = <span class="conf-val">"University of Alabama College of Engineering"</span><br>
<span class="conf-key">PGP_KEY</span> = <span class="conf-val">"0x4A8E92F1C0B3D4E7"</span><br><br>
<span class="conf-comment"># Active Skillset Matrix</span><br>
<span class="conf-key">DOMAINS</span> = [<span class="conf-val">"Network Forensics"</span>, <span class="conf-val">"Self-Hosted Infrastructure"</span>, <span class="conf-val">"Bare-Metal Virtualization"</span>]<br>
<span class="conf-key">LANGUAGES</span> = [<span class="conf-val">"POSIX sh"</span>, <span class="conf-val">"Python"</span>, <span class="conf-val">"Go"</span>, <span class="conf-val">"Lua"</span>, <span class="conf-val">"ARM Assembly"</span>]<br>
<span class="conf-key">STACK</span> = [<span class="conf-val">"Traefik v3"</span>, <span class="conf-val">"WireGuard"</span>, <span class="conf-val">"Proxmox VE"</span>, <span class="conf-val">"TrueNAS"</span>, <span class="conf-val">"Neovim"</span>]
</div>
<h3 style="font-family: 'IBM Plex Mono', monospace; font-size: 0.95rem; margin-bottom: 12px;">System Modules:</h3>
<ul style="font-size: 0.88rem; color: var(--text-muted); line-height: 1.8; margin-bottom: 20px; list-style-position: inside;">
<li><strong style="color: var(--accent-cyan);">/dev/posix-shell-parser</strong> (v1.2.0 • Lua) — Zero-allocation AST lexer</li>
<li><strong style="color: var(--accent-cyan);">/dev/wireguard-mesh</strong> (v0.8.4 • Python) — Full-mesh overlay provisioner</li>
<li><strong style="color: var(--accent-cyan);">/dev/zero-alloc-logger</strong> (v2.1.1 • Go) — High-throughput sync.Pool logger</li>
<li><strong style="color: var(--accent-cyan);">/dev/kernel-bootstrap</strong> (v0.1.0 • ARM ASM) — Bare-metal boot setup</li>
<li><strong style="color: var(--accent-cyan);">/dev/ddns-daemon</strong> (v1.0.2 • Docker API) — Dynamic Cloudflare DNS sync</li>
</ul>
<div style="display: flex; gap: 12px;">
<a href="mailto:river@rooks.dev" class="btn-action">Email: river@rooks.dev</a>
<a href="https://git.rooks.dev" class="btn-action">Git Repositories ↗</a>
<a href="index.html" class="btn-action">Spec Catalog ↗</a>
</div>
</div>
</div>
</div>
</main>
<!-- Command Center Footer -->
<footer class="cmd-footer">
<div>Keyboard Navigation: Press <kbd>1</kbd>, <kbd>2</kbd>, or <kbd>3</kbd> to switch tabs</div>
<div>Ghostty / Zed Terminal Interface</div>
</footer>
<script>
function switchTab(tabId) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'));
if (tabId === 'services') {
document.querySelectorAll('.tab-btn')[0].classList.add('active');
document.getElementById('tab-services').classList.add('active');
} else if (tabId === 'blog') {
document.querySelectorAll('.tab-btn')[1].classList.add('active');
document.getElementById('tab-blog').classList.add('active');
} else if (tabId === 'resume') {
document.querySelectorAll('.tab-btn')[2].classList.add('active');
document.getElementById('tab-resume').classList.add('active');
}
}
document.addEventListener('keydown', (e) => {
if (e.key === '1') switchTab('services');
if (e.key === '2') switchTab('blog');
if (e.key === '3') switchTab('resume');
});
</script>
</body>
</html>
-474
View File
@@ -1,474 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Terminal Workspace Dashboard</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&family=Inter:wght@400;500;700&family=JetBrains+Mono:wght@400;500;700&display=swap');
:root {
--bg-canvas: #0d0f12;
--bg-surface: #121417;
--bg-elevated: #1c1f26;
--border-color: #2b303b;
--text-primary: #e0e6f0;
--text-muted: #8a95a5;
--accent-cyan: #00f0ff;
--accent-green: #00e676;
--accent-amber: #ffb700;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Top Mode/Status Header */
.top-bar {
background-color: var(--bg-surface);
border-bottom: 1px solid var(--border-color);
padding: 8px 16px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.82rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.mode-tag {
background-color: var(--accent-cyan);
color: var(--bg-canvas);
padding: 2px 8px;
font-weight: 700;
border-radius: 2px;
}
.filepath {
color: var(--text-primary);
margin-left: 12px;
}
.status-group {
display: flex;
align-items: center;
gap: 16px;
color: var(--text-muted);
}
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent-green);
display: inline-block;
}
/* Tiled Grid Layout */
.dashboard-grid {
display: grid;
grid-template-columns: 300px 1fr 340px;
gap: 16px;
padding: 16px;
flex: 1;
}
@media (max-width: 1024px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
}
/* Tile Container */
.tile {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
display: flex;
flex-direction: column;
}
.tile-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 8px 14px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
font-weight: 700;
color: var(--text-primary);
display: flex;
justify-content: space-between;
}
.tile-body {
padding: 16px;
flex: 1;
overflow-y: auto;
}
/* Services List (Left Tile) */
.service-card {
border: 1px solid var(--border-color);
background-color: var(--bg-canvas);
padding: 12px;
margin-bottom: 12px;
border-radius: 2px;
}
.service-head {
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
font-weight: 700;
margin-bottom: 4px;
}
.service-head a {
color: var(--text-primary);
text-decoration: none;
transition: none;
}
.service-head a:hover {
color: var(--accent-cyan);
}
.service-meta {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
.badge-online { color: var(--accent-green); }
.badge-internal { color: var(--accent-amber); }
/* Resume / Main Content (Center Tile) */
.hero-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 6px;
}
.subtitle {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
color: var(--accent-cyan);
margin-bottom: 16px;
}
.bio-prose {
font-size: 0.95rem;
max-width: 65ch;
margin-bottom: 20px;
}
.section-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1rem;
font-weight: 700;
border-bottom: 1px solid var(--border-color);
padding-bottom: 6px;
margin: 20px 0 12px 0;
}
.project-card {
border: 1px solid var(--border-color);
padding: 12px;
margin-bottom: 12px;
border-radius: 2px;
}
.project-card:hover {
background-color: var(--bg-elevated);
}
.project-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.9rem;
font-weight: 700;
color: var(--accent-cyan);
margin-bottom: 4px;
}
.tag-cluster {
display: flex;
flex-wrap: wrap;
gap: 8px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
color: var(--text-muted);
margin-top: 8px;
}
/* Notes / Log Stream (Right Tile) */
.article-item {
padding: 10px 0;
border-bottom: 1px solid var(--border-color);
}
.article-item:last-child { border-bottom: none; }
.article-date {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
}
.article-link {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.85rem;
font-weight: 700;
color: var(--text-primary);
text-decoration: none;
display: block;
margin: 2px 0;
transition: none;
}
.article-link:hover {
color: var(--accent-cyan);
}
/* Command Prompt Bar */
.cmd-footer {
background-color: var(--bg-surface);
border-top: 1px solid var(--border-color);
padding: 8px 16px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
display: flex;
align-items: center;
gap: 10px;
}
.prompt-symbol { color: var(--accent-cyan); font-weight: bold; }
.cmd-input {
background: none;
border: none;
color: var(--text-primary);
font-family: inherit;
font-size: inherit;
width: 100%;
outline: none;
}
</style>
</head>
<body>
<!-- Top Status Navigation -->
<header class="top-bar">
<div style="display: flex; align-items: center; gap: 12px;">
<span class="mode-tag">NORMAL</span>
<span class="filepath">/home/river/dashboard.workspace</span>
<span style="color: var(--border-color);">|</span>
<nav style="display: flex; gap: 8px; font-size: 0.78rem;">
<a href="index.html" style="color: var(--text-primary); text-decoration: none;">Catalog</a>
<span style="color: var(--border-color);">/</span>
<a href="homelab.html" style="color: var(--text-muted); text-decoration: none;">Homelab</a>
<span style="color: var(--border-color);">/</span>
<a href="public-services.html" style="color: var(--text-muted); text-decoration: none;">Services</a>
<span style="color: var(--border-color);">/</span>
<a href="sample-command-center.html" style="color: var(--text-muted); text-decoration: none;">Command</a>
<span style="color: var(--border-color);">/</span>
<a href="sample-dual-pane-explorer.html" style="color: var(--text-muted); text-decoration: none;">Explorer</a>
</nav>
</div>
<div class="status-group">
<span><span class="status-dot"></span> TRAEFIK: V3.1 OK</span>
<span>CLUSTER: 10GbE SFP+</span>
<span>UPTIME: 99.98%</span>
</div>
</header>
<!-- Main 3-Tile Dashboard -->
<main class="dashboard-grid">
<!-- Left Tile: Homelab & Public Services Directory -->
<section class="tile">
<div class="tile-header">
<span>/etc/services</span>
<span>[CANONICAL SERVICES]</span>
</div>
<div class="tile-body">
<div class="service-card">
<div class="service-head">
<a href="https://proxy.rooks.dev" target="_blank">Traefik Proxy (v3.1)</a>
<span class="badge-online">[ONLINE]</span>
</div>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 6px;">Edge HTTP reverse proxy with dynamic ACME wildcard TLS.</p>
<div class="service-meta">
<span>PORT: 443/tcp</span>
<span>#core</span>
</div>
</div>
<div class="service-card">
<div class="service-head">
<a href="https://nas.rooks.dev" target="_blank">TrueNAS SCALE</a>
<span class="badge-online">[ONLINE]</span>
</div>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 6px;">Primary storage pool managing 48TB ZFS RaidZ2 array.</p>
<div class="service-meta">
<span>NFS / SMB</span>
<span>#storage</span>
</div>
</div>
<div class="service-card">
<div class="service-head">
<a href="https://vault.rooks.dev" target="_blank">Vaultwarden Secrets</a>
<span class="badge-online">[E2EE]</span>
</div>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 6px;">Lightweight Rust Bitwarden API hosting credentials &amp; 2FA.</p>
<div class="service-meta">
<span>2FA / AUTH</span>
<span>#secrets</span>
</div>
</div>
<div class="service-card">
<div class="service-head">
<a href="https://git.rooks.dev" target="_blank">Forgejo Git Forge</a>
<span class="badge-online">[PUBLIC]</span>
</div>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 6px;">Self-hosted Git platform for dotfiles, playbooks &amp; CTF code.</p>
<div class="service-meta">
<span>PORT: 3000</span>
<span>#dev-git</span>
</div>
</div>
<div class="service-card">
<div class="service-head">
<a href="https://chef.rooks.dev" target="_blank">CyberChef Workbench</a>
<span class="badge-online">[PUBLIC]</span>
</div>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 6px;">Hex decoding, Base64, hashing &amp; CTF payload analysis.</p>
<div class="service-meta">
<span>OPEN WEB</span>
<span>#security-tool</span>
</div>
</div>
</div>
</section>
<!-- Center Tile: Engineering Resume & Bio -->
<section class="tile">
<div class="tile-header">
<span>~/whoami.md</span>
<span>RESUME</span>
</div>
<div class="tile-body">
<h1 class="hero-title">River Rooks</h1>
<div class="subtitle">SYSTEMS ARCHITECTURE &amp; CYBERSECURITY</div>
<p class="bio-prose">
Cybersecurity student at the University of Alabama College of Engineering.
Focusing on network forensics, self-hosted infrastructure resilience, bare-metal virtualization, and low-level Unix tooling.
</p>
<h2 class="section-title">active_stack</h2>
<div class="tag-cluster">
<span>[#network-forensics]</span>
<span>[#docker-traefik]</span>
<span>[#arch-linux]</span>
<span>[#neovim]</span>
<span>[#wireguard]</span>
<span>[#posix-sh]</span>
<span>[#python]</span>
<span>[#golang]</span>
<span>[#rust]</span>
<span>[#proxmox]</span>
<span>[#truenas]</span>
</div>
<h2 class="section-title">featured_modules</h2>
<div class="project-card">
<div class="project-title">/dev/posix-shell-parser <span style="font-size: 0.75rem; color: var(--text-muted);">[v1.2.0 • #lua]</span></div>
<p style="font-size: 0.85rem; color: var(--text-primary);">
Zero-allocation Lexer and AST parser written in Lua for parsing POSIX shell scripts inside modal terminal buffers.
</p>
</div>
<div class="project-card">
<div class="project-title">/dev/wireguard-mesh <span style="font-size: 0.75rem; color: var(--text-muted);">[v0.8.4 • #wireguard]</span></div>
<p style="font-size: 0.85rem; color: var(--text-primary);">
Automated configuration generator computing key matrices and provisioning full-mesh WireGuard private overlays across cloud and homelab nodes.
</p>
</div>
<div class="project-card">
<div class="project-title">/dev/zero-alloc-logger <span style="font-size: 0.75rem; color: var(--text-muted);">[v2.1.1 • #golang]</span></div>
<p style="font-size: 0.85rem; color: var(--text-primary);">
High-throughput structured JSON logger utilizing sync.Pool byte buffers to eliminate heap allocation GC overhead (>500k ops/sec).
</p>
</div>
</div>
</section>
<!-- Right Tile: Technical Notes & Blog Stream -->
<section class="tile">
<div class="tile-header">
<span>/var/log/articles.log</span>
<span>NOTES</span>
</div>
<div class="tile-body">
<div class="article-item">
<div class="article-date">2026-07-20 | #FIRMWARE</div>
<a href="#" class="article-link">Reverse Engineering IoT Firmware: SPI Flash Extraction</a>
<p style="font-size: 0.78rem; color: var(--text-muted);">Dumping SPI flash chips via Bus Pirate &amp; flashrom to bypass locked bootloaders.</p>
</div>
<div class="article-item">
<div class="article-date">2026-07-15 | #HOMELAB</div>
<a href="#" class="article-link">Building a High-Availability Traefik Router with Docker Compose</a>
<p style="font-size: 0.78rem; color: var(--text-muted);">Dynamic Docker provider discovery &amp; Let's Encrypt automated TLS wildcard routing.</p>
</div>
<div class="article-item">
<div class="article-date">2026-07-10 | #POSIX-SH</div>
<a href="#" class="article-link">POSIX sh vs. Bash: Portability in Modern Scripts</a>
<p style="font-size: 0.78rem; color: var(--text-muted);">Eliminating bashisms and adhering to IEEE Std 1003.1 for high-speed bootstrapping.</p>
</div>
<div class="article-item">
<div class="article-date">2026-06-28 | #CTF</div>
<a href="#" class="article-link">CTF Write-up: Heap Exploitation in glibc 2.35</a>
<p style="font-size: 0.78rem; color: var(--text-muted);">Tcache safe-linking bypass and House of Apple file structure hijacking primitives.</p>
</div>
<div class="article-item">
<div class="article-date">2026-06-14 | #NETWORKING</div>
<a href="#" class="article-link">Zero-Trust Network Isolation with WireGuard &amp; OPNsense VLANs</a>
<p style="font-size: 0.78rem; color: var(--text-muted);">Segregating untrusted IoT devices, homelab servers, and management networks.</p>
</div>
</div>
</section>
</main>
<!-- Interactive Terminal Footer Bar -->
<footer class="cmd-footer">
<span class="prompt-symbol">$</span>
<input type="text" class="cmd-input" placeholder="River Rooks Workspace • Type :help or click links to navigate..." readonly>
</footer>
</body>
</html>
-464
View File
@@ -1,464 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>River Rooks | Dual Pane Explorer</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap');
:root {
--bg-canvas: #0d0f12;
--bg-surface: #121417;
--bg-elevated: #1c1f26;
--border-color: #2b303b;
--text-primary: #e0e6f0;
--text-muted: #8a95a5;
--accent-cyan: #00f0ff;
--accent-green: #00e676;
--accent-amber: #ffb700;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.6;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Top Breadcrumb Bar */
.top-nav {
background-color: var(--bg-surface);
border-bottom: 1px solid var(--border-color);
height: 42px;
padding: 0 16px;
display: flex;
align-items: center;
justify-content: space-between;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.82rem;
}
.status-dot {
width: 8px;
height: 8px;
background-color: var(--accent-green);
display: inline-block;
margin-right: 6px;
}
/* Dual Pane Layout Container */
.split-view {
display: flex;
flex: 1;
height: calc(100vh - 42px - 28px);
}
/* Left Sidebar Tree (280px) */
.sidebar {
width: 280px;
background-color: var(--bg-surface);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.82rem;
}
.sidebar-header {
padding: 10px 14px;
border-bottom: 1px solid var(--border-color);
font-weight: 700;
color: var(--text-muted);
}
.tree-view {
padding: 12px;
flex: 1;
overflow-y: auto;
}
.tree-folder {
font-weight: 700;
color: var(--text-primary);
margin: 8px 0 4px 0;
}
.tree-item {
padding: 4px 8px 4px 16px;
color: var(--text-muted);
cursor: pointer;
border-radius: 2px;
display: flex;
justify-content: space-between;
transition: none;
}
.tree-item:hover {
color: var(--accent-cyan);
background-color: var(--bg-elevated);
}
.tree-item.active {
color: var(--accent-cyan);
background-color: var(--bg-canvas);
border-left: 2px solid var(--accent-cyan);
font-weight: 700;
}
/* Bottom ASCII Companion Panel */
.sidebar-companion {
border-top: 1px solid var(--border-color);
padding: 10px;
background-color: var(--bg-canvas);
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
color: var(--text-muted);
line-height: 1.2;
}
.companion-art {
color: var(--accent-cyan);
white-space: pre;
}
/* Right Document Viewer */
.main-pane {
flex: 1;
padding: 28px;
overflow-y: auto;
background-color: var(--bg-canvas);
}
.doc-container {
max-width: 65ch;
margin: 0 auto;
}
.doc-path {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
margin-bottom: 12px;
}
.doc-title {
font-family: 'IBM Plex Mono', monospace;
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 16px;
color: var(--text-primary);
}
.code-block {
border: 1px solid var(--border-color);
background-color: var(--bg-surface);
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
border-radius: 4px;
margin: 16px 0;
overflow: hidden;
}
.code-header {
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
padding: 6px 12px;
font-size: 0.75rem;
color: var(--text-muted);
}
.code-content {
padding: 12px;
overflow-x: auto;
}
.tag-cluster {
display: flex;
gap: 8px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
color: var(--text-muted);
margin-top: 16px;
}
/* Bottom Status Bar */
.status-bar {
height: 28px;
background-color: var(--bg-surface);
border-top: 1px solid var(--border-color);
padding: 0 16px;
display: flex;
align-items: center;
justify-content: space-between;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--text-muted);
}
</style>
</head>
<body>
<!-- Top Breadcrumbs Bar -->
<header class="top-nav">
<div style="display: flex; align-items: center; gap: 12px;">
<span class="status-dot"></span>
<span style="font-weight: 700;">RIVER ROOKS // TREE EXPLORER</span>
<span style="color: var(--border-color);">|</span>
<nav style="display: flex; gap: 8px; font-size: 0.78rem;">
<a href="index.html" style="color: var(--text-primary); text-decoration: none;">Catalog</a>
<span style="color: var(--border-color);">/</span>
<a href="homepage.html" style="color: var(--text-muted); text-decoration: none;">Sample 1</a>
<span style="color: var(--border-color);">/</span>
<a href="homelab.html" style="color: var(--text-muted); text-decoration: none;">Homelab</a>
<span style="color: var(--border-color);">/</span>
<a href="public-services.html" style="color: var(--text-muted); text-decoration: none;">Services</a>
<span style="color: var(--border-color);">/</span>
<a href="sample-dashboard.html" style="color: var(--text-muted); text-decoration: none;">Dashboard</a>
</nav>
</div>
<div>
<span style="color: var(--text-muted);">HOST: pve-node02.lan [10GbE]</span>
</div>
</header>
<!-- Dual Split View -->
<div class="split-view">
<!-- Left Explorer Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">FILE SYSTEM EXPLORER</div>
<div class="tree-view">
<div class="tree-folder">▾ /services/</div>
<div class="tree-item active" onclick="selectItem(this, 'traefik')">traefik.md <span>[SVC]</span></div>
<div class="tree-item" onclick="selectItem(this, 'truenas')">truenas.md <span>[SVC]</span></div>
<div class="tree-item" onclick="selectItem(this, 'vaultwarden')">vaultwarden.md <span>[SVC]</span></div>
<div class="tree-item" onclick="selectItem(this, 'forgejo')">forgejo.md <span>[SVC]</span></div>
<div class="tree-item" onclick="selectItem(this, 'cyberchef')">cyberchef.md <span>[SVC]</span></div>
<div class="tree-folder">▾ /notes/</div>
<div class="tree-item" onclick="selectItem(this, 'spi-flash')">spi-flash.md <span>[BLOG]</span></div>
<div class="tree-item" onclick="selectItem(this, 'ha-traefik')">ha-traefik.md <span>[BLOG]</span></div>
<div class="tree-item" onclick="selectItem(this, 'posix-sh')">posix-sh.md <span>[BLOG]</span></div>
<div class="tree-item" onclick="selectItem(this, 'ctf-heap')">ctf-heap.md <span>[BLOG]</span></div>
<div class="tree-folder">▾ /modules/</div>
<div class="tree-item" onclick="selectItem(this, 'shell-parser')">shell-parser.lua <span>[DEV]</span></div>
<div class="tree-item" onclick="selectItem(this, 'mesh-builder')">wireguard-mesh.py <span>[DEV]</span></div>
<div class="tree-item" onclick="selectItem(this, 'zero-logger')">zero-logger.go <span>[DEV]</span></div>
<div class="tree-folder">▾ /resume/</div>
<div class="tree-item" onclick="selectItem(this, 'whoami')">whoami.md <span>[CV]</span></div>
<div class="tree-item" onclick="selectItem(this, 'stack')">active_stack.conf <span>[CV]</span></div>
</div>
<!-- ASCII Companion Panel -->
<div class="sidebar-companion">
<div class="companion-art" id="mascot-art">
/\___/\
( o o ) sys-daemon
( =^= ) STATUS: IDLE
\__*__/ LOG: ready█
</div>
</div>
</aside>
<!-- Right Main Document View -->
<main class="main-pane">
<div class="doc-container" id="doc-view">
<!-- Default Content: Traefik Proxy -->
<div class="doc-path">file:///home/river/services/traefik.md</div>
<h1 class="doc-title">Service: Traefik Proxy (v3.1)</h1>
<p>
Edge HTTP reverse proxy and load balancer with dynamic Docker provider discovery and ACME Let's Encrypt TLS wildcard certificate management.
</p>
<div class="code-block">
<div class="code-header">traefik.yml</div>
<div class="code-content">
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
cloudflare:
acme:
dnsChallenge:
provider: cloudflare
</div>
</div>
<div class="tag-cluster">
<span>[#core]</span>
<span>[#docker-traefik]</span>
<span>[PORT: 443/tcp]</span>
<span>[STATUS: ONLINE]</span>
</div>
</div>
</main>
</div>
<!-- Bottom Status Bar -->
<footer class="status-bar">
<div>UTF-8 | LF | POSIX Shell</div>
<div>River Rooks • Canonical Content Dataset Compliant</div>
</footer>
<script>
const docs = {
'traefik': `
<div class="doc-path">file:///home/river/services/traefik.md</div>
<h1 class="doc-title">Service: Traefik Proxy (v3.1)</h1>
<p>Edge HTTP reverse proxy and load balancer with dynamic Docker provider discovery and ACME Let's Encrypt TLS wildcard certificate management.</p>
<div class="code-block">
<div class="code-header">traefik.yml</div>
<div class="code-content">
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
cloudflare:
acme:
dnsChallenge:
provider: cloudflare
</div>
</div>
<div class="tag-cluster"><span>[#core]</span><span>[#docker-traefik]</span><span>[PORT: 443/tcp]</span><span>[STATUS: ONLINE]</span></div>
`,
'truenas': `
<div class="doc-path">file:///home/river/services/truenas.md</div>
<h1 class="doc-title">Service: TrueNAS SCALE</h1>
<p>Primary storage pool managing 48TB ZFS RaidZ2 array across 6x 16TB enterprise SAS drives with daily snapshots and NFS/SMB exports.</p>
<div class="tag-cluster"><span>[#storage]</span><span>[#zfs]</span><span>[PORT: NFS / SMB]</span><span>[STATUS: ONLINE]</span></div>
`,
'vaultwarden': `
<div class="doc-path">file:///home/river/services/vaultwarden.md</div>
<h1 class="doc-title">Service: Vaultwarden Secrets</h1>
<p>Lightweight Rust implementation of the Bitwarden API hosting encrypted credentials, SSH keys, and TOTP authentication tokens.</p>
<div class="tag-cluster"><span>[#secrets]</span><span>[#security]</span><span>[PORT: 443/tcp]</span><span>[STATUS: 2FA/AUTH]</span></div>
`,
'forgejo': `
<div class="doc-path">file:///home/river/services/forgejo.md</div>
<h1 class="doc-title">Service: Forgejo Git Forge</h1>
<p>Self-hosted Git forge hosting homelab infrastructure-as-code repos, Ansible playbooks, dotfiles, and CTF binary writeup scripts.</p>
<div class="tag-cluster"><span>[#dev-git]</span><span>[#git]</span><span>[PORT: 3000/tcp]</span><span>[STATUS: PUBLIC]</span></div>
`,
'cyberchef': `
<div class="doc-path">file:///home/river/services/cyberchef.md</div>
<h1 class="doc-title">Service: CyberChef Workbench</h1>
<p>Web-based data transformation Swiss Army knife for hex decoding, Base64 operations, regex extraction, and CTF payload analysis.</p>
<div class="tag-cluster"><span>[#security-tool]</span><span>[#crypto]</span><span>[STATUS: PUBLIC]</span></div>
`,
'spi-flash': `
<div class="doc-path">file:///home/river/notes/iot-firmware-spi-extraction.md</div>
<h1 class="doc-title">Reverse Engineering IoT Firmware: SPI Flash Extraction</h1>
<p>Physical hardware-level dumping of SPI flash memory chips using Bus Pirate and flashrom to bypass locked bootloaders.</p>
<div class="code-block">
<div class="code-header">extract_firmware.sh</div>
<div class="code-content">
flashrom -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=1M -r firmware_dump.bin
md5sum firmware_dump.bin
</div>
</div>
<div class="tag-cluster"><span>[#firmware]</span><span>[#hardware]</span><span>[#security]</span><span>[DATE: 2026-07-20]</span></div>
`,
'ha-traefik': `
<div class="doc-path">file:///home/river/notes/ha-traefik-docker.md</div>
<h1 class="doc-title">Building a High-Availability Traefik Router</h1>
<p>Architecting automated TLS wildcard certificate propagation and dynamic Docker container discovery failover routing.</p>
<div class="tag-cluster"><span>[#homelab]</span><span>[#docker-traefik]</span><span>[DATE: 2026-07-15]</span></div>
`,
'posix-sh': `
<div class="doc-path">file:///home/river/notes/posix-sh-vs-bash.md</div>
<h1 class="doc-title">POSIX sh vs. Bash: Portability in Modern Scripts</h1>
<p>Eliminating bashisms and adhering to IEEE Std 1003.1 for high-speed bootstrapping in Alpine and embedded targets.</p>
<div class="tag-cluster"><span>[#posix-sh]</span><span>[#unix]</span><span>[DATE: 2026-07-10]</span></div>
`,
'ctf-heap': `
<div class="doc-path">file:///home/river/notes/ctf-heap-glibc-2-35.md</div>
<h1 class="doc-title">CTF Write-up: Heap Exploitation in glibc 2.35</h1>
<p>Tcache safe-linking bypass and House of Apple file structure hijacking primitives for arbitrary code execution.</p>
<div class="tag-cluster"><span>[#ctf]</span><span>[#binary-exploitation]</span><span>[DATE: 2026-06-28]</span></div>
`,
'shell-parser': `
<div class="doc-path">file:///home/river/modules/posix-shell-parser.lua</div>
<h1 class="doc-title">Module: Custom POSIX Shell Parser (v1.2.0)</h1>
<p>Zero-allocation Lexer and AST parser written in Lua for real-time syntax coloring in modal editors.</p>
<div class="code-block">
<div class="code-header">lexer.lua</div>
<div class="code-content">
local function tokenize(src)
local tokens = {}
for pos, char in ipairs(src) do
if char == ";" or char == "|" then table.insert(tokens, { type = "OPERATOR", value = char }) end
end
return tokens
end
</div>
</div>
<div class="tag-cluster"><span>[#lua]</span><span>[#ast-parser]</span><span>[VERSION: v1.2.0]</span></div>
`,
'mesh-builder': `
<div class="doc-path">file:///home/river/modules/wireguard-mesh.py</div>
<h1 class="doc-title">Module: WireGuard Mesh Topology Builder (v0.8.4)</h1>
<p>Matrix topology generator creating atomic configuration files and key pairings across multi-cloud and homelab nodes.</p>
<div class="tag-cluster"><span>[#networking]</span><span>[#wireguard]</span><span>[VERSION: v0.8.4]</span></div>
`,
'zero-logger': `
<div class="doc-path">file:///home/river/modules/zero-alloc-logger.go</div>
<h1 class="doc-title">Module: Zero-Allocation Logger (Go v2.1.1)</h1>
<p>High-throughput structured JSON logger utilizing sync.Pool byte buffers to eliminate heap allocations (>500k ops/sec).</p>
<div class="tag-cluster"><span>[#golang]</span><span>[#performance]</span><span>[VERSION: v2.1.1]</span></div>
`,
'whoami': `
<div class="doc-path">file:///home/river/resume/whoami.md</div>
<h1 class="doc-title">River Rooks - Systems Architecture &amp; Cybersecurity</h1>
<p>Cybersecurity student at the University of Alabama College of Engineering, focusing on network forensics, self-hosted infrastructure, and bare-metal virtualization.</p>
<div class="tag-cluster"><span>[#cybersecurity]</span><span>[#systems]</span><span>[UA COLLEGE OF ENGINEERING]</span></div>
`,
'stack': `
<div class="doc-path">file:///home/river/resume/active_stack.conf</div>
<h1 class="doc-title">Active Engineering Stack</h1>
<div class="code-block">
<div class="code-header">stack.conf</div>
<div class="code-content">
DOMAINS="Network Forensics, Self-Hosted Infrastructure, Linux Kernel Hardening"
LANGUAGES="POSIX sh, Python, Go, Rust, Lua, ARM Assembly"
STACK="Traefik v3, WireGuard, Proxmox VE, TrueNAS SCALE, Neovim"
</div>
</div>
<div class="tag-cluster"><span>[#arch-linux]</span><span>[#neovim]</span><span>[#wireguard]</span></div>
`
};
function selectItem(element, docKey) {
document.querySelectorAll('.tree-item').forEach(el => el.classList.remove('active'));
element.classList.add('active');
document.getElementById('doc-view').innerHTML = docs[docKey] || '';
// Update mascot state
const mascot = document.getElementById('mascot-art');
mascot.innerText = ` /\\___/\\\n ( - - ) sys-daemon\n ( =o= ) STATUS: READING\n \\__*__/ LOG: ${docKey}_`;
setTimeout(() => {
mascot.innerText = ` /\\___/\\\n ( o o ) sys-daemon\n ( =^= ) STATUS: IDLE\n \\__*__/ LOG: ready█`;
}, 1200);
}
</script>
</body>
</html>
-106
View File
@@ -1,106 +0,0 @@
# Design Specification: Sample 3 (Companion Layout)
**Status:** Ready for Handoff
**Target Page:** `companion.html`
**Layout Type:** 50/50 Split Screen (Reactive Desktop, Stacked Mobile)
**Theme Vibe:** Hyper-minimalist terminal utility, clean information hierarchy, zero animation noise.
---
## 1. Design Philosophy & Guidelines
This sample modifies the primary design spec by enforcing a strict **"interaction suppression"** policy. Dynamic decorations (such as border color transitions or card expansion effects) are completely disallowed. Contrast and active states are communicated solely through instantaneous typography color changes.
The key feature is a persistent digital companion on the right half of the screen. The companion must feel like a quiet terminal process rather than an active distraction.
---
## 2. Layout Geometry & Grid
The layout divides the viewport down the middle on desktop screens.
### Desktop Layout (Width > 768px)
- **Top Header:** Sticky `56px` navigation bar containing breadcrumbs/catalog links.
- **Split Container:** Spans 100% viewport width and 100% viewport height minus header (`height: calc(100vh - 56px)`).
- **Columns:**
- **Left Pane (50%):** Scrollable container. Houses a single-column list of project cards.
- **Right Pane (50%):** Fixed/non-scrollable container. Houses the centered ASCII companion terminal widget.
### Mobile Layout (Width <= 768px)
- The layout collapses into a single vertical stack.
- The ASCII companion pane moves to the top (fixed height: `300px`).
- The content pane follows below, scrollable within the main document window.
---
## 3. Color Palette & Typography
### Base Colors
- **Canvas Background:** `#0d0f12` (Deep charcoal slate)
- **Surface Background:** `#121417` (Slightly lighter dark surface)
- **Static Borders:** `#2b303b` (1px solid, low contrast)
- **Primary Text:** `#e0e6f0` (Off-white prose)
- **Muted Text:** `#8a95a5` (Timestamp / status information)
- **Active Accent:** `#00f0ff` (High-contrast Cyan)
### Typography
- **UI & ASCII Content:** Monospace (`JetBrains Mono` or `IBM Plex Mono`).
- **Prose Content:** Sans-serif (`Inter` or `Geist Sans`) for high readability.
---
## 4. Content Cards (Left Column)
Each card is a clean, structural text panel.
### Structure
- 1px static border (`#2b303b`) with `4px` border-radius.
- No background color (inherits Canvas background) or a solid `#121417` surface background.
- Elements:
1. **Header (`h3`):** Monospaced, bold, `#e0e6f0`.
2. **Description (`p`):** Sans-serif, `#8a95a5`, capped at `65ch` length.
### Interactions (Ultra-Minimal)
- **No Border Shifts:** Borders must remain `#2b303b` at all times.
- **No Translation:** Hovering must not scale, shift, or offset the card.
- **Instant Typography Snap:** Hovering or focusing the card snaps the text color of the Header (`h3`) from `#e0e6f0` to Cyan (`#00f0ff`) instantly.
- **CSS Rule:** Use `transition: none;` on all hover effects.
---
## 5. ASCII Companion (Right Column)
The companion is rendered as block-level ASCII text inside a simulated terminal panel.
### Layout Widget
- Sits centered in the right pane.
- Styled to resemble a terminal output terminal: card box with a tab bar indicating the daemon name `sys-daemon`.
### Animation & States
The companion has two interactive states:
#### State 1: IDLE
- **Mascot Art:**
```text
/\___/\
( o o ) sys-daemon
( =^= ) STATUS: IDLE
\__*__/ LOG: awaiting input█
```
- **Animation:** The terminal block cursor (`█`) blinks continuously at 1Hz (`opacity: 0` to `1` instantly). The rest of the mascot is static.
#### State 2: READING (Triggered on Card Hover)
- **Mascot Art:**
```text
/\___/\
( - - ) sys-daemon
( =o= ) STATUS: READING
\__*__/ LOG: reading module_
```
- **Reaction Log:** Underneath the mascot, a mini status output log updates to display the title of the card currently hovered. For example:
`[sys-daemon] target: /dev/mesh-mesh-topology-builder`
### Behavior Scripting Rules
- Attach `mouseenter` and `mouseleave` event listeners to the cards in the left pane.
- On hover, swap the inner HTML of the ASCII container to the `READING` frame and append the card's target name to the log output.
- Revert back to the `IDLE` frame immediately when the cursor leaves the card.
-478
View File
@@ -1,478 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimalist Split Panel Layout</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
/* Design Spec Token Definition */
:root {
--bg-canvas: #121417;
--bg-surface: #1c1f26;
--border-color: #2b303b;
--text-primary: #e0e6f0;
--text-muted: #8a95a5;
--accent: #00ff66; /* ANSI Green */
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-canvas);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
line-height: 1.6;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* Sticky Navigation Header at the top */
.sticky-header {
position: sticky;
top: 0;
height: 56px;
background-color: var(--bg-canvas);
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
z-index: 100;
font-family: 'IBM Plex Mono', monospace;
}
.logo-container {
font-weight: 700;
font-size: 1rem;
color: var(--text-primary);
}
.logo-prompt {
color: var(--accent);
}
.header-nav {
display: flex;
gap: 16px;
}
.nav-link {
color: var(--text-primary);
text-decoration: none;
font-size: 0.9rem;
padding: 4px 10px;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
}
/* Snappy Hover Inversion */
.nav-link:hover {
background-color: var(--accent);
color: var(--bg-canvas);
}
.nav-link:focus {
outline: 2px solid var(--accent);
outline-offset: -2px;
}
/* Structural Indicator for Active Navigation Items */
.nav-link.active::before {
content: "> ";
color: var(--accent);
margin-right: 4px;
}
.nav-link.active:hover::before {
color: var(--bg-canvas);
}
/* Split Container Layout */
.split-container {
display: flex;
flex: 1;
height: calc(100vh - 56px);
overflow: hidden;
}
@media (max-width: 768px) {
.split-container {
flex-direction: column;
overflow-y: auto;
}
.left-pane {
width: 100% !important;
height: auto !important;
border-right: none !important;
border-bottom: 1px solid var(--border-color);
padding: 40px 24px !important;
}
.right-pane {
height: auto !important;
overflow-y: visible !important;
}
}
/* Left Pane - Sticky Terminal Mascot (non-scrolling) */
.left-pane {
width: 380px;
background-color: var(--bg-canvas);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 32px;
flex-shrink: 0;
}
.terminal-card {
width: 100%;
border: 1px solid var(--border-color);
background-color: var(--bg-surface);
border-radius: 4px;
padding: 20px;
font-family: 'IBM Plex Mono', monospace;
}
.terminal-card-header {
font-size: 0.75rem;
color: var(--text-muted);
margin-bottom: 16px;
border-bottom: 1px solid var(--border-color);
padding-bottom: 8px;
display: flex;
justify-content: space-between;
}
.mascot-pre {
font-size: 0.8rem;
line-height: 1.2;
color: var(--accent);
background-color: var(--bg-canvas);
padding: 12px;
border: 1px solid var(--border-color);
border-radius: 2px;
overflow: hidden;
white-space: pre;
}
.status-panel {
margin-top: 16px;
font-size: 0.8rem;
color: var(--text-primary);
}
.status-row {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
}
.status-label {
color: var(--text-muted);
}
.status-value {
font-weight: 500;
}
/* Right Pane - Scrollable Content feed */
.right-pane {
flex: 1;
overflow-y: auto;
background-color: var(--bg-canvas);
padding: 40px 24px;
display: flex;
justify-content: center;
}
.scroll-content {
width: 100%;
max-width: 70ch; /* Cap readability width */
}
.section-title {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
font-size: 1.4rem;
color: var(--text-primary);
margin-bottom: 24px;
border-bottom: 1px solid var(--border-color);
padding-bottom: 8px;
}
/* Tile Cards Component */
.tile-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.tile {
background-color: var(--bg-surface);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 24px;
text-decoration: none;
color: inherit;
display: block;
transition: none; /* Instant snap interactions */
}
/* Snappy Hover State (Border highlight & accent title) */
.tile:hover {
border-color: var(--accent);
}
.tile:focus {
outline: 2px solid var(--accent);
outline-offset: 4px;
}
.tile-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 8px;
}
.tile-title {
font-family: 'IBM Plex Mono', monospace;
font-weight: 700;
font-size: 1.1rem;
color: var(--text-primary);
}
.tile:hover .tile-title {
color: var(--accent);
}
.tile-meta {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
}
.tile-description {
font-size: 0.9rem;
color: var(--text-primary);
line-height: 1.5;
}
/* Footer styling inside the scroll view */
.pane-footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.8rem;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
</style>
</head>
<body>
<!-- Sticky Top Header -->
<header class="sticky-header">
<div class="logo-container">
<span class="logo-prompt">$</span> agy --layout=split
</div>
<nav class="header-nav">
<a href="index.html" class="nav-link">Catalog</a>
<a href="homepage.html" class="nav-link">Sample 1</a>
<a href="split-panel.html" class="nav-link active">Sample 2</a>
<a href="companion.html" class="nav-link">Sample 3</a>
<a href="document-flow.html" class="nav-link">Sample 4</a>
<a href="portfolio.html" class="nav-link">Sample 5</a>
<a href="homelab.html" class="nav-link">Sample 6</a>
<a href="public-services.html" class="nav-link">Sample 7</a>
<a href="sample-dashboard.html" class="nav-link">Sample 8</a>
<a href="sample-command-center.html" class="nav-link">Sample 9</a>
<a href="sample-dual-pane-explorer.html" class="nav-link">Sample 10</a>
</nav>
</header>
<!-- Split Panel Main Container -->
<div class="split-container">
<!-- Left Pane - Center-Aligned ASCII Art Terminal Mascot -->
<aside class="left-pane">
<div class="terminal-card">
<div class="terminal-card-header">
<span>sys_monitor.sh</span>
<span>ONLINE</span>
</div>
<pre id="ascii-mascot" class="mascot-pre">
.-----------------.
/ ^ ^ ^ /|
/ (_) (_) (_) / |
/__________________/ |
| ___ ___ | |
| [o_o] [o_o] | |
| `-' `-' | |
| ______ | |
| / \ | /
| | -- | | /
| \____/ |/
`------------------'
</pre>
<div class="status-panel">
<div class="status-row">
<span class="status-label">Target:</span>
<span class="status-value">pve-node02.lan</span>
</div>
<div class="status-row">
<span class="status-label">Status:</span>
<span class="status-value" style="color: var(--accent);">ANIMATING</span>
</div>
<div class="status-row">
<span class="status-label">Cluster:</span>
<span class="status-value">10GbE SFP+</span>
</div>
</div>
</div>
</aside>
<!-- Right Pane - Scrollable List of Cards -->
<main class="right-pane">
<div class="scroll-content">
<h2 class="section-title">River Rooks // System Modules</h2>
<div class="tile-list">
<!-- Tile 1 -->
<a href="#" class="tile">
<div class="tile-header">
<h3 class="tile-title">Custom POSIX Shell Parser</h3>
<span class="tile-meta">v1.2.0 • [#lua]</span>
</div>
<p class="tile-description">
A lightweight Lexer and AST parser written in Lua for parsing POSIX-compliant shell scripts. Highly optimized for syntax coloring inside modal environments without runtime heap allocations.
</p>
</a>
<!-- Tile 2 -->
<a href="#" class="tile">
<div class="tile-header">
<h3 class="tile-title">WireGuard Mesh Topology Builder</h3>
<span class="tile-meta">v0.8.4 • [#wireguard]</span>
</div>
<p class="tile-description">
Automated configuration generator for computing key matrices and setting up full-mesh WireGuard private overlays across cloud instances and local homelab nodes.
</p>
</a>
<!-- Tile 3 -->
<a href="#" class="tile">
<div class="tile-header">
<h3 class="tile-title">Zero-Allocation Logger (Go)</h3>
<span class="tile-meta">v2.1.1 • [#golang]</span>
</div>
<p class="tile-description">
A high-performance JSON structure logger that uses sync.Pool buffers to avoid memory allocation overhead during intensive disk operations (>500k ops/sec).
</p>
</a>
<!-- Tile 4 -->
<a href="#" class="tile">
<div class="tile-header">
<h3 class="tile-title">Embedded Kernel Boot Strap</h3>
<span class="tile-meta">v0.1.0 • [#arm-cortex]</span>
</div>
<p class="tile-description">
Minimalist boot sector and initial kernel bootstrap for ARM Cortex-M microcontrollers to handle vector table offset mapping and early UART status printouts.
</p>
</a>
<!-- Tile 5 -->
<a href="#" class="tile">
<div class="tile-header">
<h3 class="tile-title">Traefik Cloudflare Dynamic DNS Daemon</h3>
<span class="tile-meta">v1.0.2 • [#docker-traefik]</span>
</div>
<p class="tile-description">
Background controller daemon designed to sync Docker container network labels with Cloudflare DNS records to route traffic to local homelab subdomains dynamically.
</p>
</a>
</div>
<footer class="pane-footer">
<span>Sample 2: Split Panel Layout</span>
<span>© 2026 River Rooks</span>
</footer>
</div>
</main>
</div>
<!-- Lightweight Script for Mascot Animation (Binary Steps) -->
<script>
const mascot = document.getElementById('ascii-mascot');
const frames = [
// Frame 1: Standard Look
` .-----------------.
/ ^ ^ ^ /|
/ (_) (_) (_) / |
/__________________/ |
| ___ ___ | |
| [o_o] [o_o] | |
| `-' `-' | |
| ______ | |
| / \\ | /
| | -- | | /
| \\____/ |/
\`------------------'`,
// Frame 2: Blinking Eyes / Surprised Mouth
` .-----------------.
/ ^ ^ ^ /|
/ (_) (_) (_) / |
/__________________/ |
| ___ ___ | |
| [-_-] [-_-] | |
| \`-' \`-' | |
| ______ | |
| / \\ | /
| | O | | /
| \\____/ |/
\`------------------'`,
// Frame 3: Winking Eyes / Grinning Mouth
` .-----------------.
/ ^ ^ ^ /|
/ (_) (_) (_) / |
/__________________/ |
| ___ ___ | |
| [o_-] [o_-] | |
| \`-' \`-' | |
| ______ | |
| / \\ | /
| | == | | /
| \\____/ |/
\`------------------'`
];
let currentFrame = 0;
setInterval(() => {
currentFrame = (currentFrame + 1) % frames.length;
mascot.innerText = frames[currentFrame];
}, 850);
</script>
</body>
</html>
-86
View File
@@ -1,86 +0,0 @@
# Unified Design Specification: Homelab, Blog & Engineering Portfolio
**Target Platform:** Integrated Personal Site (Public Services Directory, Technical Blog/Notes, Engineering Resume)
**Design Philosophy:** High-signal, low-noise. Developer-centric minimalism, modal text editor & terminal emulator craftsmanship.
**Vibe References:** Zed Editor, Ghostty, Noctalia.dev, Tiling Window Managers (Hyprland/i3).
**Strict Anti-Patterns:** Zero glassmorphism, zero glows/shadows, zero gradients, zero spring animations, zero rounded SaaS "blobs".
---
## 1. Executive Summary & Unified Purpose
This specification unites the design language across three primary user goals:
1. **Services Directory:** Instant access, routing links, and status visibility for self-hosted homelab services and public utilities.
2. **Technical Notes & Blog:** High-density, comfortable reading experience for deep technical writing (CTF write-ups, architecture specs, config guides, code snippets).
3. **Engineering Resume & Portfolio:** A crisp, high-craft presentation of skills, project archives, and active tech stack designed to impress technical recruiters and engineering leaders.
---
## 2. Unified Design Language & Rules
### Global Constraints
* **NO Glassmorphism:** Zero `backdrop-filter: blur()`, frosted glass, or translucent overlays.
* **NO Glows or Drop Shadows:** Depth is constructed exclusively via subtle surface color contrast and 1px solid borders.
* **NO Gradients:** Pure, solid color tokens across canvas, surfaces, and accent elements.
* **NO Spring/Float Animations:** Interactions feature instant snapped state transitions (`transition: none;`) or binary 1Hz blinking cursors/indicators.
* **Minimal Radii:** Sharp corners preferred. Border radii capped strictly at `0px` to `4px`.
### Geometry & Spatial Hierarchy
* **Grid Baseline:** Strict 4pt / 8pt baseline grid for consistent spacing and padding.
* **Reading Density:** Article and prose containers capped at `65ch` max width.
* **Dividers:** Clean 1px solid horizontal lines (`<hr>`) used to delineate major structural sections.
---
## 3. Unified Color Palette
The color system is dark-mode native, built from muted charcoal slate tones reminiscent of modern terminal emulators and modal text editors.
| Token Key | Hex Value | Application & Role |
| :--- | :--- | :--- |
| `--bg-canvas` | `#0d0f12` | Main canvas background (deep charcoal slate). |
| `--bg-surface` | `#121417` | Card backgrounds, terminal blocks, sidebars. |
| `--bg-elevated` | `#1c1f26` | Hovered card state, active tab surface. |
| `--border-color` | `#2b303b` | Structural 1px grid lines, card borders, horizontal dividers. |
| `--text-primary` | `#e0e6f0` | Off-white primary text for headings and body prose. |
| `--text-muted` | `#8a95a5` | Monospaced metadata, timestamps, tags, labels, status text. |
| `--accent-cyan` | `#00f0ff` | Primary active accent (terminal prompt, active nav snap, focus outline). |
| `--accent-green` | `#00e676` | Status indicator: Online / Operational / Verified. |
| `--accent-amber` | `#ffb700` | Status indicator: Under Construction / Maintenance / Warning. |
---
## 4. Digital & Typographical Language
### Dual Typography Hierarchy
* **Monospace Layer (`IBM Plex Mono`, `JetBrains Mono`):**
- **Usage:** Headings (`H1`-`H3`), navigation links, breadcrumbs, tags, terminal code blocks, timestamps, metadata, and keyboard shortcuts (`<kbd>`).
- **Weights:** Medium (500), Bold (700).
* **Prose Layer (`Inter`, `Geist Sans`):**
- **Usage:** Article body prose, project summaries, resume biography.
- **Line Height:** `1.65` to `1.7`.
- **Weight:** Regular (400).
### Digital Interface Conventions
* **POSIX-Style Path Headers:** Titles and section markers formatted as file paths (e.g., `/var/log/status.txt`, `/etc/services`, `~/notes/ctf-2026.md`).
* **Active Status Dots:** Rigid 8x8px square indicators (`.status-dot`) signaling system state (Cyan/Green for operational, Amber for construction).
* **Metadata Tag Cluster:** Monospaced bracketed tags (e.g., `[#homelab]`, `[#docker-traefik]`, `[#cybersecurity]`).
* **Instantaneous Snap Hover:** Interactive elements transition without delay (`transition: none;`), snapping text to Cyan (`#00f0ff`) or inverting background colors.
* **Keyboard Shortcut Badges (`<kbd>`):** Crisp, 1px bordered boxes depicting keys (e.g., `Ctrl` + `K`, `Tab`).
---
## 5. Functional Layout Modules
### A. Homelab & Public Services Directory
* Dense tabular grid listing self-hosted apps and public APIs.
* Displays service name, description, status badge (`[ONLINE]`, `[INTERNAL]`, `[PUBLIC]`), and routing link.
### B. Technical Notes & Blog
* Tabular article archive indexed by date, title, reading duration, and tags.
* Terminal-style code snippets with filename headers (e.g., `traefik.yml`) and copy utility.
### C. Resume & Portfolio Showcase
* Header identity block with `whoami` summary and active stack cluster.
* Project showcase cards featuring instant snap state.
* Optional integrated ASCII companion daemon (`sys-daemon`) for interactive module inspection.