Compare commits
5
Commits
server
...
9aa0ada81d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9aa0ada81d | ||
|
|
7bf4d98fc0 | ||
|
|
73603bc61b | ||
|
|
bd6358cf1c | ||
|
|
42e7e0f1ff |
+8
-3
@@ -17,12 +17,17 @@ RUN npm ci
|
|||||||
|
|
||||||
# Copy application source files
|
# Copy application source files
|
||||||
COPY . .
|
COPY . .
|
||||||
|
RUN chmod +x /app/entrypoint.sh
|
||||||
|
|
||||||
# Build static production assets
|
# Pre-compile baseline static assets
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Expose web server port dynamically
|
# Expose web server port dynamically
|
||||||
EXPOSE ${PORT}
|
EXPOSE ${PORT}
|
||||||
|
|
||||||
# Serve site with npm
|
# Native container healthcheck probing IPv4 loopback
|
||||||
CMD ["npm", "start"]
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=3s --retries=3 \
|
||||||
|
CMD node -e "fetch('http://127.0.0.1:' + (process.env.PORT || 8080) + '/healthz').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
|
||||||
|
|
||||||
|
# Serve site using dynamic entrypoint
|
||||||
|
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||||
|
|||||||
@@ -1,116 +1,223 @@
|
|||||||
# River Rooks | Technical Publication & Systems Architecture Blog
|
# River Rooks | Technical Publication & Systems Architecture Blog
|
||||||
|
|
||||||
> Developer-centric, high-signal, low-noise personal technical blog and homelab documentation site.
|
> Developer-centric, high-signal, low-noise personal technical publication and systems architecture blog.
|
||||||
|
|
||||||
Inspired by native terminal environments, modal text editors (Zed, Ghostty), and tiling window managers, this site prioritizes raw performance, keyboard navigability, information density, and structured craftsmanship over generic marketing aesthetics.
|
Inspired by native terminal environments, modal text editors (Zed, Ghostty), and tiling window managers, this site prioritizes raw performance, keyboard navigability, information density, and structured craftsmanship over generic marketing aesthetics.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Architectural Vision & Pillars
|
## 1. Quickstart: Production Server Deployment
|
||||||
|
|
||||||
### 1.1 Static Generation via Eleventy (11ty)
|
The production deployment uses a **decoupled container architecture**. The server does not need Node.js, git, or build dependencies—it only pulls the prebuilt engine from the registry and mounts a local folder containing your Markdown posts.
|
||||||
- **Engine:** Fast, pre-rendered static HTML and minimal client-side runtime using **Eleventy** and **Nunjucks (`.njk`)** templates.
|
|
||||||
- **Performance:** Instant time-to-first-byte (TTFB), zero client-side framework bloat, and pure static asset delivery via high-performance native Node runtime.
|
|
||||||
- **Syntax Highlighting & Code Blocks:** Terminal-styled code blocks with title bars displaying filenames, language tags, and high-contrast monospace code.
|
|
||||||
|
|
||||||
### 1.2 Dynamic Obsidian Publishing Pipeline
|
|
||||||
- **Seamless Authoring:** Author and edit technical dispatches, CTF write-ups, and homelab notes directly inside an **Obsidian** vault.
|
|
||||||
- **Custom Ingestion API:** A dedicated server-side endpoint (`POST /api/posts`) accepting markdown payloads and assets, paired with a custom Obsidian plugin for one-click publishing.
|
|
||||||
- **LiveSync Integration:** Architectural support for syncing notes from a self-hosted Obsidian LiveSync (CouchDB) instance.
|
|
||||||
- **Content Normalization:** Translates Obsidian wikilinks (`[[slug|Title]]`), callouts (`> [!NOTE]`), and asset references into standard web formats during ingestion and build time.
|
|
||||||
|
|
||||||
### 1.3 Strict CSS Isolation & Reusable Templating
|
|
||||||
- **No Monolithic Inlines:** Replaces repetitive inline `<style>` tags with a modular CSS token and component architecture.
|
|
||||||
- **Design Tokens:** Centralized CSS custom properties (`--bg-canvas`, `--bg-surface`, `--border-color`, `--accent-cyan`, etc.) for consistent dark-mode native styling.
|
|
||||||
- **DRY Layouts & Partials:** Reusable Nunjucks components for global navigation (`global-bar`), hero tiles (`hero-tile`), split cards, and article workspace layouts (`article-workspace-grid`).
|
|
||||||
|
|
||||||
### 1.4 AI Pair Architect & Pedagogical Explainer
|
|
||||||
- **Interactive Learning:** The workspace includes customized Antigravity agent configurations (`.agents/` and `AGENTS.md`) that pair-program with the author, architecting solutions while clearly explaining *how* and *why* code is built.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Strict Design Specification
|
|
||||||
|
|
||||||
Adhering to the principles in [`sample-designs/design-specification.md`](./sample-designs/design-specification.md):
|
|
||||||
|
|
||||||
| Rule | Constraint |
|
|
||||||
| :--- | :--- |
|
|
||||||
| **Color Scheme** | Dark-mode native only. Canvas: `#0d0f12`, Surface: `#121417`, Elevated: `#1c1f26`, Borders: `#2b303b`. |
|
|
||||||
| **Accents** | High-signal terminal accents used sparingly: Cyan (`#00f0ff`), Green (`#00e676`), Amber (`#ffb700`). |
|
|
||||||
| **Borders & Radius** | Crisp 1px solid borders. Zero to 4px maximum border-radius (sharp corners preferred). |
|
|
||||||
| **Strict Anti-Patterns** | **NO** glassmorphism (`backdrop-filter`), **NO** drop shadows or glows, **NO** gradients, **NO** spring animations. |
|
|
||||||
| **Typography** | Monospace for UI & code (`IBM Plex Mono`, `JetBrains Mono`); clean sans-serif for body prose (`Inter`). |
|
|
||||||
| **Interactivity** | Instant binary hover snaps (no slow fade transitions); high-contrast focus outlines for keyboard navigation. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Project Directory Map
|
|
||||||
|
|
||||||
|
### 1.1 Server Directory Structure (`~/docker/blog`)
|
||||||
```text
|
```text
|
||||||
.
|
~/docker/blog/
|
||||||
├── .agents/ # Antigravity agent configurations & customizations
|
├── .env # Environment variables (SITE_DOMAIN, etc.)
|
||||||
│ ├── rules/
|
├── docker-compose.yml # Production container definition
|
||||||
│ │ └── blog-system.md # Architectural constraints, CSS/template isolation & explainer mode
|
└── posts/ # Your live Markdown articles (.md)
|
||||||
│ └── skills/
|
```
|
||||||
│ ├── eleventy-tiling-blog/ # Runbook & recipes for Eleventy builds and component extraction
|
|
||||||
│ └── obsidian-sync/ # Runbook & API contracts for Obsidian publishing & plugin sync
|
### 1.2 Production `docker-compose.yml`
|
||||||
├── sample-designs/ # Canonical HTML/CSS prototypes & design specification
|
```yaml
|
||||||
│ ├── article-tiling-firmware.html
|
services:
|
||||||
│ ├── blog-tiling-lead.html
|
web:
|
||||||
│ ├── design-specification.md
|
image: git.riverrooks.dev/venus/river-rooks-blog:latest
|
||||||
│ └── service-gateway-tiling.html
|
container_name: river_rooks_web
|
||||||
├── src/ # Eleventy source directory (scaffolded in Phase 2)
|
restart: unless-stopped
|
||||||
│ ├── _data/ # Global site metadata, navigation, telemetry data
|
volumes:
|
||||||
│ ├── _includes/
|
- "./posts:/app/src/posts:ro"
|
||||||
│ │ ├── layouts/ # Base, post, and page templates
|
environment:
|
||||||
│ │ └── partials/ # Global bar, hero tile, card, code box, sidebar
|
- "SITE_DOMAIN=${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env}"
|
||||||
│ ├── assets/
|
- "SITE_URL=${SITE_URL:-https://${SITE_DOMAIN}}"
|
||||||
│ │ ├── css/ # Modular CSS (tokens, reset, layout, components)
|
- "PORT=${PORT:-8080}"
|
||||||
│ │ └── fonts/ # Self-hosted monospace & prose webfonts
|
- "WATCH=${WATCH:-false}"
|
||||||
│ └── posts/ # Markdown content ingested from Obsidian
|
ports:
|
||||||
├── .eleventy.js # Eleventy configuration, markdown-it plugins, and shortcodes
|
- "${HOST_PORT:-${PORT:-8080}}:${PORT:-8080}"
|
||||||
├── package.json # Project dependencies & build scripts
|
networks:
|
||||||
├── server.js # Native Node static web server & clean-URL router
|
- backend
|
||||||
├── Dockerfile # Node container definition (built & served via npm)
|
labels:
|
||||||
├── docker-compose.yml # Container orchestration (web gateway)
|
- "traefik.enable=true"
|
||||||
└── AGENTS.md # Project-level agent instructions & pair programming guide
|
- "traefik.http.routers.blog.rule=Host(`riverrooks.dev`) || Host(`www.riverrooks.dev`)"
|
||||||
|
- "traefik.http.routers.blog.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.blog.tls.certresolver=basic"
|
||||||
|
- "traefik.http.services.blog.loadbalancer.server.port=8080"
|
||||||
|
- "traefik.docker.network=backend"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backend:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Deployment Commands
|
||||||
|
```bash
|
||||||
|
# 1. Pull the prebuilt engine from Gitea registry
|
||||||
|
docker compose pull
|
||||||
|
|
||||||
|
# 2. Launch the container
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 3. Verify healthcheck status (transitions to healthy in 3-5 seconds)
|
||||||
|
docker compose ps
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Development & Build Roadmap
|
## 2. Local Development & Verification
|
||||||
|
|
||||||
- [x] **Phase 1: Foundation & Agent Customizations**
|
All templates, styles, and engine code are developed in this repository (`~/code/blog`).
|
||||||
- Establish `README.md` architectural blueprint.
|
|
||||||
- Setup `.agents/` workspace rules, skills, and root `AGENTS.md`.
|
|
||||||
- [ ] **Phase 2: Eleventy Scaffolding & Modularization**
|
|
||||||
- Initialize Node/npm environment and configure `.eleventy.js`.
|
|
||||||
- Extract design tokens, layout styles, and components into `src/assets/css/`.
|
|
||||||
- Build reusable Nunjucks layouts and partials modeled on `sample-designs/`.
|
|
||||||
- [ ] **Phase 3: Dynamic Obsidian Ingestion & Plugin**
|
|
||||||
- Implement the server-side note ingestion endpoint (`POST /api/posts`).
|
|
||||||
- Develop the custom Obsidian plugin for publishing notes and frontmatter.
|
|
||||||
- Integrate wikilink and callout transformation filters.
|
|
||||||
- [ ] **Phase 4: Containerization & Deployment**
|
|
||||||
- Verify static build outputs in `_site/`.
|
|
||||||
- Update Dockerfile and `docker-compose.yml` for unified production serving.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Development Setup
|
|
||||||
|
|
||||||
*(To be initialized during Phase 2)*
|
|
||||||
|
|
||||||
|
### 2.1 Native Node.js Workflow
|
||||||
```bash
|
```bash
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# Start Eleventy local development server with hot-reload
|
# Run automated test suite (unit tests, filters, server, and docker assertions)
|
||||||
|
npm test
|
||||||
|
|
||||||
|
# Launch Eleventy dev server with hot-reload (binds to http://localhost:8080)
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# Compile static assets for production
|
# Compile static assets to _site/
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
# Run via Docker Compose
|
# Start the native static server locally
|
||||||
docker compose up --build
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 2.2 Local Docker Compose Workflow
|
||||||
|
The local `docker-compose.yml` provides sensible defaults (`localhost`, port 8080, and mounts `./src/posts`):
|
||||||
|
```bash
|
||||||
|
# Build and run the local container stack
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# View container logs (Eleventy compilation + server output)
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Stop and clean up
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Push & Update Cycles
|
||||||
|
|
||||||
|
Because the system separates the **Engine** (templates/code) from the **Content** (markdown notes), updates follow two distinct paths:
|
||||||
|
|
||||||
|
### 3.1 Publishing or Editing Articles (Content Workflow)
|
||||||
|
When you write a new note or edit an existing article:
|
||||||
|
|
||||||
|
1. **Add or update the Markdown file** in `posts/` (e.g. `posts/my-new-post.md`):
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: "My Article Title"
|
||||||
|
date: 2026-09-14
|
||||||
|
tags: ["#systems", "#architecture"]
|
||||||
|
excerpt: "A short synopsis of the article."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Article Content...
|
||||||
|
```
|
||||||
|
2. **Recompile the site:**
|
||||||
|
```bash
|
||||||
|
docker compose restart web
|
||||||
|
```
|
||||||
|
*Eleventy compiles the mounted markdown in ~150ms and starts serving immediately.*
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> **Zero-Restart Live Reload:** Set `WATCH=true` in your server's `docker-compose.yml` environment. Eleventy will watch the `posts/` directory in the background. Whenever a note is saved or synced (e.g., from Obsidian), the site automatically updates in memory without running any commands.
|
||||||
|
|
||||||
|
### 3.2 Updating Blog Templates, CSS, or Features (Engine Workflow)
|
||||||
|
When you modify design tokens, Nunjucks templates, server routes, or dependencies:
|
||||||
|
|
||||||
|
1. **Develop and verify locally:**
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
2. **Build and push the new engine image to the Gitea container registry:**
|
||||||
|
```bash
|
||||||
|
# Build the engine image
|
||||||
|
docker build -t git.riverrooks.dev/venus/river-rooks-blog:latest .
|
||||||
|
|
||||||
|
# Push to Gitea
|
||||||
|
docker push git.riverrooks.dev/venus/river-rooks-blog:latest
|
||||||
|
```
|
||||||
|
3. **Update the production server:**
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. System Architecture & Structure
|
||||||
|
|
||||||
|
### 4.1 High-Level Architecture
|
||||||
|
```text
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Dev Machine / Workstation │
|
||||||
|
│ src/ (templates, CSS, config) + Dockerfile + entrypoint.sh │
|
||||||
|
└──────────────────────────────┬──────────────────────────────┘
|
||||||
|
│ docker push
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Gitea Container Registry (git.riverrooks.dev) │
|
||||||
|
│ image: venus/river-rooks-blog:latest │
|
||||||
|
└──────────────────────────────┬──────────────────────────────┘
|
||||||
|
│ docker pull
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Production Server │
|
||||||
|
│ │
|
||||||
|
│ ~/docker/blog/posts/ ──(volume)──> [ river_rooks_web ] │
|
||||||
|
│ (Live Markdown Notes) ├── entrypoint.sh │
|
||||||
|
│ ├── 11ty compiles │
|
||||||
|
│ Traefik (SSL / Proxy) <──────────── └── server.js (:8080) │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Key Engine Components
|
||||||
|
- **`entrypoint.sh`**: Dynamic container entrypoint. Compiles mounted markdown notes with Eleventy on boot, starts optional background file watcher, and spawns `server.js` as PID 1 for clean signal handling.
|
||||||
|
- **`Dockerfile`**: Minimal `node:22-alpine` container with pre-installed dependencies, baseline static pre-compilation, and baked-in `HEALTHCHECK` querying `http://127.0.0.1:8080/healthz`.
|
||||||
|
- **`server.js`**: Zero-dependency native Node static web server supporting clean directory URLs, MIME caching, security headers, and the `/healthz` telemetry endpoint.
|
||||||
|
- **`eleventy.config.js`**: Eleventy 3.x configuration, custom collections (`posts`, `tagList`), markdown-it anchor plugins, and custom date/reading-time filters.
|
||||||
|
|
||||||
|
### 4.3 Directory Map
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── .agents/ # Antigravity agent configurations & rules
|
||||||
|
├── sample-designs/ # Design specifications & HTML/CSS prototypes
|
||||||
|
├── src/ # Blog engine source
|
||||||
|
│ ├── _data/ # Site metadata & navigation
|
||||||
|
│ ├── _filters/ # Modular Eleventy filters (date, readingTime, headings)
|
||||||
|
│ ├── _includes/
|
||||||
|
│ │ ├── layouts/ # Base & post Nunjucks templates
|
||||||
|
│ │ └── partials/ # Global bar, hero tile, post cards, code box
|
||||||
|
│ ├── assets/
|
||||||
|
│ │ └── css/ # Modular CSS (tokens, reset, layout, components)
|
||||||
|
│ └── posts/ # Default/fallback markdown posts
|
||||||
|
├── test/ # Automated test suite (node:test)
|
||||||
|
├── docker-compose.yml # Local development compose file
|
||||||
|
├── sample-docker-compose.yml # Production-ready compose template
|
||||||
|
├── Dockerfile # Production container image definition
|
||||||
|
├── entrypoint.sh # Container startup & compilation entrypoint
|
||||||
|
├── server.js # Native Node static HTTP server
|
||||||
|
├── eleventy.config.js # Eleventy configuration
|
||||||
|
├── package.json # Project scripts & dependencies
|
||||||
|
└── README.md # Documentation & runbook
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Strict Design Specification
|
||||||
|
|
||||||
|
All UI components and stylesheets strictly follow [`sample-designs/design-specification.md`](./sample-designs/design-specification.md):
|
||||||
|
|
||||||
|
| Rule | Constraint |
|
||||||
|
| :--- | :--- |
|
||||||
|
| **Color Scheme** | Dark-mode native only. Canvas: `#0d0f12`, Surface: `#121417`, Elevated: `#1c1f26`, Borders: `#2b303b`. |
|
||||||
|
| **Accents** | Terminal accents: Cyan (`#00f0ff`), Green (`#00e676`), Amber (`#ffb700`), Red (`#ff5252`). |
|
||||||
|
| **Borders & Radius** | Crisp 1px solid borders. Zero to 4px maximum border-radius (sharp corners). |
|
||||||
|
| **Strict Anti-Patterns** | **NO** glassmorphism (`backdrop-filter`), **NO** drop shadows or glows, **NO** gradients, **NO** spring animations. |
|
||||||
|
| **Typography** | Monospace for UI & code (`IBM Plex Mono`, `JetBrains Mono`); clean sans-serif for body prose (`Inter`). |
|
||||||
|
| **Interactivity** | Instant binary hover snaps (no slow fade transitions); high-contrast focus outlines for keyboard navigation. |
|
||||||
|
|||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Pass-through mode: if arguments were provided, execute them directly
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
exec "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build static site from mounted or packaged markdown posts
|
||||||
|
echo "[blog-engine] Compiling static site with Eleventy..."
|
||||||
|
npm run build || exit 1
|
||||||
|
|
||||||
|
# Optional watch mode for zero-restart live note updates
|
||||||
|
if [ "${WATCH:-false}" = "true" ]; then
|
||||||
|
echo "[blog-engine] Starting live watch daemon for markdown changes..."
|
||||||
|
npx @11ty/eleventy --watch &
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start production static web server
|
||||||
|
echo "[blog-engine] Starting static server on port ${PORT:-8080}..."
|
||||||
|
exec node server.js
|
||||||
@@ -106,6 +106,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-pretitle {
|
.hero-pretitle {
|
||||||
|
|||||||
@@ -407,11 +407,11 @@
|
|||||||
<!-- Main Hero Landing Tile (Central Focus of Homepage) -->
|
<!-- Main Hero Landing Tile (Central Focus of Homepage) -->
|
||||||
<section class="hero-tile" aria-label="Homepage Mission & Landing Declaration">
|
<section class="hero-tile" aria-label="Homepage Mission & Landing Declaration">
|
||||||
<div class="hero-pretitle">SYSTEM DECLARATION • RACK-01 NODE GATEWAY</div>
|
<div class="hero-pretitle">SYSTEM DECLARATION • RACK-01 NODE GATEWAY</div>
|
||||||
<h1 class="hero-main-title">Engineering Sovereign Infrastructure & Technical Notes</h1>
|
<h1 class="hero-main-title">Cybersecurity & Dev Ops notes</h1>
|
||||||
|
|
||||||
<div class="hero-landing-text">
|
<div class="hero-landing-text">
|
||||||
<p>
|
<p>
|
||||||
I am River Rooks, a cybersecurity engineering student at the University of Alabama. My work centers on network forensics, self-hosted system architecture, low-level firmware exploitation, and zero-allocation Unix utilities.
|
I am River Rooks, a cybersecurity student at the University of Alabama. My work centers on self-hosted system architecture, network security, and capture the flag style skills. I maintain a personal homelab cluster for research, experimentation, and development of open-source tools.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
This website functions as the unified frontend for my physical homelab cluster. Rather than hosting projects on opaque third-party clouds, everything here runs on bare metal in my personal server rack—orchestrated through Proxmox VE, partitioned across isolated VLANs via OPNsense, and protected by point-to-point WireGuard cryptographic meshes.
|
This website functions as the unified frontend for my physical homelab cluster. Rather than hosting projects on opaque third-party clouds, everything here runs on bare metal in my personal server rack—orchestrated through Proxmox VE, partitioned across isolated VLANs via OPNsense, and protected by point-to-point WireGuard cryptographic meshes.
|
||||||
|
|||||||
+27
-14
@@ -1,25 +1,38 @@
|
|||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build:
|
# Pull prebuilt engine image from your Gitea registry
|
||||||
context: .
|
image: git.riverrooks.dev/venus/river-rooks-blog:latest
|
||||||
dockerfile: Dockerfile
|
# Local build fallback (optional when building image locally)
|
||||||
args:
|
# build:
|
||||||
SITE_DOMAIN: "${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env}"
|
# context: .
|
||||||
SITE_URL: "${SITE_URL:-https://${SITE_DOMAIN}}"
|
# dockerfile: Dockerfile
|
||||||
PORT: "${PORT:-8080}"
|
# args:
|
||||||
image: river-rooks-blog:latest
|
# 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}"
|
||||||
container_name: river_rooks_web
|
container_name: river_rooks_web
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
# Healthcheck is natively baked into the Dockerfile image metadata
|
||||||
test: ["CMD", "node", "test/healthcheck.js"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 3s
|
|
||||||
start_period: 3s
|
|
||||||
retries: 3
|
|
||||||
environment:
|
environment:
|
||||||
- "SITE_DOMAIN=${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env}"
|
- "SITE_DOMAIN=${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env}"
|
||||||
- "SITE_URL=${SITE_URL:-https://${SITE_DOMAIN}}"
|
- "SITE_URL=${SITE_URL:-https://${SITE_DOMAIN}}"
|
||||||
- "PORT=${PORT:-8080}"
|
- "PORT=${PORT:-8080}"
|
||||||
|
- "WATCH=${WATCH:-false}"
|
||||||
|
volumes:
|
||||||
|
# Mount external markdown posts directly into the container
|
||||||
|
- "./posts:/app/src/posts:ro"
|
||||||
ports:
|
ports:
|
||||||
- "${HOST_PORT:-${PORT:-8080}}:${PORT:-8080}"
|
- "${HOST_PORT:-${PORT:-8080}}:${PORT:-8080}"
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.blog.rule=Host(`riverrooks.dev`) || Host(`www.riverrooks.dev`)"
|
||||||
|
- "traefik.http.routers.blog.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.blog.tls.certresolver=basic"
|
||||||
|
- "traefik.http.services.blog.loadbalancer.server.port=8080"
|
||||||
|
- "traefik.docker.network=backend"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backend:
|
||||||
|
external: true
|
||||||
|
|||||||
+9
-3
@@ -54,8 +54,8 @@ export default {
|
|||||||
url: "/dispatches/"
|
url: "/dispatches/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Gitea",
|
label: "Code",
|
||||||
url: `https://git.${siteDomain}`,
|
url: `https://git.riverrooks.dev/venus`,
|
||||||
external: true
|
external: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -66,7 +66,7 @@ export default {
|
|||||||
services: [
|
services: [
|
||||||
{
|
{
|
||||||
name: "Gitea",
|
name: "Gitea",
|
||||||
url: `https://git.${siteDomain}`,
|
url: `https://git.riverrooks.dev/venus`,
|
||||||
tag: `git.${siteDomain}:2222`,
|
tag: `git.${siteDomain}:2222`,
|
||||||
desc: "Self-hosted Git code repositories."
|
desc: "Self-hosted Git code repositories."
|
||||||
},
|
},
|
||||||
@@ -93,6 +93,12 @@ export default {
|
|||||||
url: `https://library.${siteDomain}`,
|
url: `https://library.${siteDomain}`,
|
||||||
tag: `books.${siteDomain}:443`,
|
tag: `books.${siteDomain}:443`,
|
||||||
desc: `Calibre web frontend for managing and reading eBooks.`
|
desc: `Calibre web frontend for managing and reading eBooks.`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Whoami",
|
||||||
|
url: `https://whoami.${siteDomain}`,
|
||||||
|
tag: `whoami.${siteDomain}:443`,
|
||||||
|
desc: `Simple service to display request headers and connection information.`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
topics: [
|
topics: [
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-pretitle {
|
.hero-pretitle {
|
||||||
|
|||||||
+5
-3
@@ -6,15 +6,17 @@ title: "River Rooks | Homelab Gateway"
|
|||||||
<!-- Main Hero Landing Tile -->
|
<!-- Main Hero Landing Tile -->
|
||||||
<section class="hero-tile" aria-label="Gateway Declaration">
|
<section class="hero-tile" aria-label="Gateway Declaration">
|
||||||
<div class="hero-pretitle">SYSTEM DECLARATION • NODE GATEWAY</div>
|
<div class="hero-pretitle">SYSTEM DECLARATION • NODE GATEWAY</div>
|
||||||
<h1 class="hero-main-title">Engineering Sovereign Infrastructure & Technical Notes</h1>
|
<h1 class="hero-main-title">Cybersecurity & Dev Ops notes</h1>
|
||||||
|
|
||||||
<div class="hero-landing-text">
|
<div class="hero-landing-text">
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum.
|
I am River Rooks, a cybersecurity student and the University of Alabama. This website is the public-facing gateway to my homelab, which hosts a variety of services and tools for personal and professional use. Here, you can find my technical blog and a collection of public tools that host.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
I deploy and maintain a variety of services in my homelab for research, development, and learning purposes. This includes web applications, databases, and other tools that I use to enhance my skills in cybersecurity and DevOps.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
My blog serves as a way to share some of my extensive notes with the public. I primarily focus on Capture The Flag (CTF) writeups, technical guides, and other cybersecurity-related content. I also use this platform to document my learning journey and share insights into the world of cybersecurity and DevOps.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hero-directive-box">
|
<div class="hero-directive-box">
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
---
|
|
||||||
layout: layouts/post.njk
|
|
||||||
title: "Infrastructure Notes & Systems Deployment Guide"
|
|
||||||
date: 2026-07-15
|
|
||||||
tags:
|
|
||||||
- homelab
|
|
||||||
- networking
|
|
||||||
- templates
|
|
||||||
excerpt: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident."
|
|
||||||
readingTime: "4 MIN READ"
|
|
||||||
featured: false
|
|
||||||
difficulty: "BEGINNER"
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1.0 Topology Overview
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua:
|
|
||||||
|
|
||||||
```text
|
|
||||||
[Gateway Ingress]
|
|
||||||
|
|
|
||||||
[Edge Router]
|
|
||||||
|
|
|
||||||
+-----+-----+
|
|
||||||
| |
|
|
||||||
[Node 01] [Node 02]
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2.0 Deployment Configuration
|
|
||||||
|
|
||||||
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: service:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "8080:80"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3.0 Verification Testing
|
|
||||||
|
|
||||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -s http://localhost:8080/health
|
|
||||||
```
|
|
||||||
|
|
||||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
---
|
|
||||||
layout: layouts/post.njk
|
|
||||||
title: "Sample Technical Dispatch: Systems Architecture & Implementation"
|
|
||||||
date: 2026-07-20
|
|
||||||
tags:
|
|
||||||
- systems
|
|
||||||
- architecture
|
|
||||||
- templates
|
|
||||||
excerpt: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
|
|
||||||
readingTime: "6 MIN READ"
|
|
||||||
featured: true
|
|
||||||
difficulty: "INTERMEDIATE"
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1.0 Executive Summary
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla.
|
|
||||||
|
|
||||||
Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa.
|
|
||||||
|
|
||||||
<div class="markdown-callout callout-note">
|
|
||||||
<div class="callout-title">[!NOTE] Operational Directives</div>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## 2.0 System Parameters & Specifications
|
|
||||||
|
|
||||||
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam nec ante. Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing diam, a cursus ipsum ante quis turpis:
|
|
||||||
|
|
||||||
### 2.1 Interface Configuration Matrix
|
|
||||||
|
|
||||||
| Parameter ID | Signal Name | Protocol Bus | Expected Value |
|
|
||||||
| :--- | :--- | :--- | :--- |
|
|
||||||
| **0x01** | `SYS_CLOCK` | SPI Primary | 100 MHz |
|
|
||||||
| **0x02** | `DATA_IN` | Serial Bus | Bidirectional |
|
|
||||||
| **0x03** | `CONTROL_REG` | I2C Header | 0x3F Active |
|
|
||||||
| **0x04** | `GROUND_REF` | Chassis GND | 0.0V Nominal |
|
|
||||||
|
|
||||||
<div class="markdown-callout callout-warning">
|
|
||||||
<div class="callout-title">[!WARNING] Configuration Voltage Boundary</div>
|
|
||||||
<p>Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## 3.0 Execution Workflow
|
|
||||||
|
|
||||||
Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
#!/bin/sh
|
|
||||||
# Sample deployment and verification routine
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
echo "[+] Initializing subsystem..."
|
|
||||||
check_environment() {
|
|
||||||
test -f /etc/config.json && echo "Config verified."
|
|
||||||
}
|
|
||||||
|
|
||||||
check_environment
|
|
||||||
```
|
|
||||||
|
|
||||||
Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to interrupt operations safely at any point.
|
|
||||||
|
|
||||||
## 4.0 Telemetry & Verification
|
|
||||||
|
|
||||||
Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla:
|
|
||||||
|
|
||||||
- **Metric A:** `1,048,576 ops/sec`
|
|
||||||
- **Metric B:** `SHA256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
|
|
||||||
- **Latency:** `< 2.5ms` nominal
|
|
||||||
|
|
||||||
## 5.0 Audit Checklist
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit:
|
|
||||||
|
|
||||||
- [x] Baseline architectural parameters verified
|
|
||||||
- [x] Initial configuration and unit tests passed
|
|
||||||
- [x] System telemetry within expected boundaries
|
|
||||||
- [ ] Production deployment and final review
|
|
||||||
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
test("Docker Configuration: Dockerfile enforces Node runtime and excludes Nginx", () => {
|
test("Docker Configuration: Dockerfile enforces Node runtime, healthcheck, and entrypoint", () => {
|
||||||
const dockerfilePath = path.resolve(process.cwd(), "Dockerfile");
|
const dockerfilePath = path.resolve(process.cwd(), "Dockerfile");
|
||||||
assert.ok(fs.existsSync(dockerfilePath), "Dockerfile must exist");
|
assert.ok(fs.existsSync(dockerfilePath), "Dockerfile must exist");
|
||||||
|
|
||||||
@@ -16,12 +16,32 @@ test("Docker Configuration: Dockerfile enforces Node runtime and excludes Nginx"
|
|||||||
assert.doesNotMatch(content, /FROM\s+nginx/i, "Dockerfile must not use nginx base image");
|
assert.doesNotMatch(content, /FROM\s+nginx/i, "Dockerfile must not use nginx base image");
|
||||||
assert.doesNotMatch(content, /nginx/i, "Dockerfile must not contain nginx directives");
|
assert.doesNotMatch(content, /nginx/i, "Dockerfile must not contain nginx directives");
|
||||||
|
|
||||||
// Must build static site and serve with npm
|
// Must build static site and configure dynamic port
|
||||||
assert.match(content, /RUN\s+npm\s+run\s+build/, "Dockerfile must run npm build");
|
assert.match(content, /RUN\s+npm\s+run\s+build/, "Dockerfile must compile baseline static assets");
|
||||||
assert.match(content, /ARG\s+PORT=8080/, "Dockerfile must define ARG PORT with default fallback");
|
assert.match(content, /ARG\s+PORT=8080/, "Dockerfile must define ARG PORT with default fallback");
|
||||||
assert.match(content, /ENV\s+PORT=\$\{?PORT\}?/, "Dockerfile must define ENV PORT using variable");
|
assert.match(content, /ENV\s+PORT=\$\{?PORT\}?/, "Dockerfile must define ENV PORT using variable");
|
||||||
assert.match(content, /EXPOSE\s+\$\{?PORT\}?/, "Dockerfile must expose dynamic PORT variable");
|
assert.match(content, /EXPOSE\s+\$\{?PORT\}?/, "Dockerfile must expose dynamic PORT variable");
|
||||||
assert.match(content, /CMD\s*\[\s*["']npm["']\s*,\s*["']start["']\s*\]/, "Dockerfile must start via npm start");
|
|
||||||
|
// Must define built-in HEALTHCHECK probing 127.0.0.1 loopback
|
||||||
|
assert.match(content, /HEALTHCHECK[\s\S]*?127\.0\.0\.1/, "Dockerfile must define baked-in HEALTHCHECK on 127.0.0.1");
|
||||||
|
|
||||||
|
// Must configure dynamic entrypoint script
|
||||||
|
assert.match(content, /ENTRYPOINT\s*\[\s*["']\/app\/entrypoint\.sh["']\s*\]/, "Dockerfile must use /app/entrypoint.sh entrypoint");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Docker Configuration: entrypoint.sh script exists and has valid structure", () => {
|
||||||
|
const entrypointPath = path.resolve(process.cwd(), "entrypoint.sh");
|
||||||
|
assert.ok(fs.existsSync(entrypointPath), "entrypoint.sh must exist");
|
||||||
|
|
||||||
|
const stats = fs.statSync(entrypointPath);
|
||||||
|
// Check executable permissions (at least owner executable: 0o100)
|
||||||
|
assert.ok(Boolean(stats.mode & 0o111), "entrypoint.sh must have executable permission bits");
|
||||||
|
|
||||||
|
const content = fs.readFileSync(entrypointPath, "utf-8");
|
||||||
|
assert.match(content, /^#!\/bin\/sh/, "entrypoint.sh must use POSIX /bin/sh shebang");
|
||||||
|
assert.match(content, /exec\s+"\$@"/, "entrypoint.sh must pass through custom container arguments");
|
||||||
|
assert.match(content, /npm\s+run\s+build/, "entrypoint.sh must build the static site");
|
||||||
|
assert.match(content, /exec\s+node\s+server\.js/, "entrypoint.sh must start static server with exec");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Docker Configuration: docker-compose configures Node service correctly", () => {
|
test("Docker Configuration: docker-compose configures Node service correctly", () => {
|
||||||
@@ -33,9 +53,12 @@ test("Docker Configuration: docker-compose configures Node service correctly", (
|
|||||||
const content = fs.readFileSync(composePath, "utf-8");
|
const content = fs.readFileSync(composePath, "utf-8");
|
||||||
|
|
||||||
// Verifies dynamic port mapping and environment variables
|
// Verifies dynamic port mapping and environment variables
|
||||||
assert.match(content, /PORT:\s*"?\$\{PORT:-8080\}"?/, "docker-compose.yml must pass dynamic PORT build arg and env");
|
assert.match(content, /PORT.*8080/, "docker-compose.yml must configure PORT fallback");
|
||||||
assert.match(content, /:\$\{PORT:-8080\}/, "docker-compose.yml must map dynamic container PORT");
|
assert.match(content, /:\$\{PORT:-8080\}/, "docker-compose.yml must map dynamic container PORT");
|
||||||
|
|
||||||
// Verifies domain argument enforcement
|
// Verifies domain argument enforcement
|
||||||
assert.match(content, /SITE_DOMAIN/, "docker-compose.yml must require SITE_DOMAIN");
|
assert.match(content, /SITE_DOMAIN/, "docker-compose.yml must require SITE_DOMAIN");
|
||||||
|
|
||||||
|
// Verifies markdown volume mount
|
||||||
|
assert.match(content, /\/app\/src\/posts/, "docker-compose.yml must mount external markdown posts");
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user