first commit
This commit is contained in:
52
templates/about.html
Normal file
52
templates/about.html
Normal file
@@ -0,0 +1,52 @@
|
||||
{{define "title"}}About — Ridgway Systems{{end}}
|
||||
{{define "meta-desc"}}About Ridgway Systems — a personal OpenBSD homelab project.{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="page-header">
|
||||
<h1>About</h1>
|
||||
</div>
|
||||
|
||||
<div class="prose">
|
||||
<p>
|
||||
Ridgway Systems is a personal homelab project built entirely on OpenBSD. The goal is to self-host
|
||||
as many services as practical on owned hardware, with a focus on simplicity, security, and
|
||||
understanding every layer of the stack.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This site documents the build — hardware choices, configuration decisions, and things learned
|
||||
along the way. If you're setting up your own homelab or migrating to OpenBSD, hopefully something
|
||||
here is useful.
|
||||
</p>
|
||||
|
||||
<h2>Why OpenBSD?</h2>
|
||||
<ul>
|
||||
<li>Security-first design. <code>pledge(2)</code> and <code>unveil(2)</code> are excellent.</li>
|
||||
<li>Clean, minimal base system. No surprises.</li>
|
||||
<li><code>pf(4)</code> is the best firewall I've used.</li>
|
||||
<li>Documentation is thorough and accurate. The man pages are genuinely good.</li>
|
||||
<li>Deliberate, careful development. The OpenBSD team doesn't chase hype.</li>
|
||||
</ul>
|
||||
|
||||
<h2>What's Running</h2>
|
||||
<p>
|
||||
See the <a href="/infrastructure">infrastructure page</a> for the full hardware and service list.
|
||||
Briefly: a SuperMicro 1U as the firewall/router, a Dell R720 as the primary server, and a
|
||||
Dell R710 for backup and game servers. Everything is managed with Ansible.
|
||||
</p>
|
||||
|
||||
<h2>Can I see the Ansible playbooks?</h2>
|
||||
<p>
|
||||
Eventually. The playbooks are on the <a href="https://git.ridgwaysystems.org">Gitea instance</a>
|
||||
(private for now while things are in flux). Plan is to open them up once they're in a state
|
||||
I'm not embarrassed by.
|
||||
</p>
|
||||
|
||||
<h2>Contact</h2>
|
||||
<ul class="contact-list">
|
||||
<li>Email: <a href="mailto:bridgway@ridgwaysystems.org">bridgway@ridgwaysystems.org</a></li>
|
||||
<li>Gitea: <a href="https://git.ridgwaysystems.org">git.ridgwaysystems.org</a></li>
|
||||
<li>Mastodon: <a href="https://mastodon.social/@bridgway" rel="me">@bridgway@mastodon.social</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
55
templates/admin/dashboard.html
Normal file
55
templates/admin/dashboard.html
Normal file
@@ -0,0 +1,55 @@
|
||||
{{define "title"}}Admin Dashboard — Ridgway Systems{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="admin-wrap">
|
||||
<div class="admin-header">
|
||||
<h1>Admin Dashboard</h1>
|
||||
<div class="admin-actions">
|
||||
<a href="/admin/new" class="btn">New Post</a>
|
||||
<a href="/admin/status" class="btn btn-outline">Edit Status</a>
|
||||
<a href="/" class="btn btn-outline">View Site</a>
|
||||
<form method="POST" action="/admin/logout" class="inline-form">
|
||||
<button type="submit" class="btn btn-outline">Logout</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .Flash}}
|
||||
<p class="flash-msg">{{.Flash}}</p>
|
||||
{{end}}
|
||||
|
||||
<h2>Posts</h2>
|
||||
{{if .Posts}}
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
<th>Tags</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Posts}}
|
||||
<tr>
|
||||
<td><a href="/blog/{{.Slug}}">{{.Title}}</a></td>
|
||||
<td>{{formatDate .ParsedDate}}</td>
|
||||
<td>{{if .Draft}}<span class="draft-badge">draft</span>{{else}}<span class="pub-badge">published</span>{{end}}</td>
|
||||
<td class="tags-cell">{{range .Tags}}<span class="tag">#{{.}}</span> {{end}}</td>
|
||||
<td class="actions-cell">
|
||||
<a href="/admin/edit/{{.Slug}}" class="btn btn-sm">Edit</a>
|
||||
<form method="POST" action="/admin/delete/{{.Slug}}" class="inline-form"
|
||||
onsubmit="return confirm('Delete {{.Title}}?')">
|
||||
<button type="submit" class="btn btn-sm btn-danger">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<p class="empty-state">No posts yet. <a href="/admin/new">Create the first one.</a></p>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
104
templates/admin/editor.html
Normal file
104
templates/admin/editor.html
Normal file
@@ -0,0 +1,104 @@
|
||||
{{define "title"}}{{if .IsNew}}New Post{{else}}Edit Post{{end}} — Admin{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="admin-wrap">
|
||||
<div class="admin-header">
|
||||
<h1>{{if .IsNew}}New Post{{else}}Edit: {{if .Post}}{{.Post.Title}}{{end}}{{end}}</h1>
|
||||
<div class="admin-actions">
|
||||
<a href="/admin" class="btn btn-outline">Back to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .Error}}
|
||||
<p class="form-error">{{.Error}}</p>
|
||||
{{end}}
|
||||
|
||||
<form method="POST" action="{{if .IsNew}}/admin/new{{else}}/admin/edit/{{if .Post}}{{.Post.Slug}}{{end}}{{end}}" class="editor-form" id="post-form">
|
||||
{{if .IsNew}}
|
||||
<div class="form-row">
|
||||
<label for="slug">Slug (filename, no .md)</label>
|
||||
<input type="text" id="slug" name="slug" placeholder="my-post-slug" pattern="[a-z0-9\-_]+" required>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="editor-toolbar">
|
||||
<input type="file" id="img-file" accept="image/*" style="display:none">
|
||||
<button type="button" id="upload-btn" class="btn btn-sm btn-outline">Insert Image</button>
|
||||
<span id="upload-status" class="upload-status"></span>
|
||||
</div>
|
||||
|
||||
<div class="editor-layout">
|
||||
<div class="editor-pane">
|
||||
<label for="content">Markdown</label>
|
||||
<textarea id="content" name="content" class="editor-textarea" spellcheck="false">{{.Raw}}</textarea>
|
||||
</div>
|
||||
<div class="preview-pane">
|
||||
<div class="preview-label">Preview <button type="button" id="preview-btn" class="btn btn-sm">Refresh</button></div>
|
||||
<div id="preview-output" class="preview-output prose"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-footer">
|
||||
<button type="submit" class="btn">Save</button>
|
||||
<a href="/admin" class="btn btn-outline">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var previewBtn = document.getElementById('preview-btn');
|
||||
var uploadBtn = document.getElementById('upload-btn');
|
||||
var imgFile = document.getElementById('img-file');
|
||||
var textarea = document.getElementById('content');
|
||||
var output = document.getElementById('preview-output');
|
||||
var uploadStatus = document.getElementById('upload-status');
|
||||
|
||||
// --- Preview ---
|
||||
function refreshPreview() {
|
||||
var fd = new FormData();
|
||||
fd.append('content', textarea.value);
|
||||
fetch('/admin/preview', { method: 'POST', body: fd })
|
||||
.then(function(r) { return r.text(); })
|
||||
.then(function(html) { output.innerHTML = html; })
|
||||
.catch(function() { output.innerHTML = '<p class="form-error">Preview failed.</p>'; });
|
||||
}
|
||||
|
||||
previewBtn.addEventListener('click', refreshPreview);
|
||||
if (textarea.value.trim()) { refreshPreview(); }
|
||||
|
||||
// --- Image upload ---
|
||||
uploadBtn.addEventListener('click', function() { imgFile.click(); });
|
||||
|
||||
imgFile.addEventListener('change', function() {
|
||||
if (!this.files.length) return;
|
||||
var file = this.files[0];
|
||||
var fd = new FormData();
|
||||
fd.append('image', file);
|
||||
|
||||
uploadStatus.textContent = 'Uploading…';
|
||||
uploadBtn.disabled = true;
|
||||
|
||||
fetch('/admin/upload', { method: 'POST', body: fd })
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.error) {
|
||||
uploadStatus.textContent = 'Error: ' + data.error;
|
||||
return;
|
||||
}
|
||||
// Insert markdown at cursor position
|
||||
var pos = textarea.selectionStart;
|
||||
var before = textarea.value.substring(0, pos);
|
||||
var after = textarea.value.substring(textarea.selectionEnd);
|
||||
textarea.value = before + data.markdown + after;
|
||||
textarea.selectionStart = textarea.selectionEnd = pos + data.markdown.length;
|
||||
textarea.focus();
|
||||
uploadStatus.textContent = 'Inserted: ' + data.url;
|
||||
setTimeout(function() { uploadStatus.textContent = ''; }, 3000);
|
||||
})
|
||||
.catch(function() { uploadStatus.textContent = 'Upload failed.'; })
|
||||
.finally(function() { uploadBtn.disabled = false; imgFile.value = ''; });
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
15
templates/admin/login.html
Normal file
15
templates/admin/login.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{{define "title"}}Admin Login — Ridgway Systems{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="admin-login-wrap">
|
||||
<h1>Admin</h1>
|
||||
{{if .Error}}
|
||||
<p class="form-error">{{.Error}}</p>
|
||||
{{end}}
|
||||
<form method="POST" action="/admin/login" class="login-form">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" autofocus required>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
33
templates/admin/status.html
Normal file
33
templates/admin/status.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{{define "title"}}Edit Status — Admin{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="admin-wrap">
|
||||
<div class="admin-header">
|
||||
<h1>Edit Service Status</h1>
|
||||
<div class="admin-actions">
|
||||
<a href="/admin" class="btn btn-outline">Back</a>
|
||||
<a href="/status" class="btn btn-outline">View Status Page</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .Flash}}
|
||||
<p class="flash-msg">{{.Flash}}</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Error}}
|
||||
<p class="form-error">{{.Error}}</p>
|
||||
{{end}}
|
||||
|
||||
<p class="page-desc">
|
||||
Edit the raw JSON below. Valid status values: <code>up</code>, <code>degraded</code>,
|
||||
<code>down</code>, <code>unknown</code>.
|
||||
</p>
|
||||
|
||||
<form method="POST" action="/admin/status">
|
||||
<textarea name="json" class="json-editor" rows="30" spellcheck="false">{{.JSON}}</textarea>
|
||||
<div class="editor-footer">
|
||||
<button type="submit" class="btn">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
49
templates/base.html
Normal file
49
templates/base.html
Normal file
@@ -0,0 +1,49 @@
|
||||
{{define "base"}}<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{block "title" .}}Ridgway Systems{{end}}</title>
|
||||
<meta name="description" content="{{block "meta-desc" .}}A homelab built on OpenBSD — from firewall to git server.{{end}}">
|
||||
<!-- OpenGraph -->
|
||||
<meta property="og:site_name" content="Ridgway Systems">
|
||||
<meta property="og:title" content="{{block "og-title" .}}Ridgway Systems{{end}}">
|
||||
<meta property="og:description" content="{{block "og-desc" .}}A homelab built on OpenBSD — from firewall to git server.{{end}}">
|
||||
<meta property="og:type" content="{{block "og-type" .}}website{{end}}">
|
||||
<meta property="og:url" content="{{block "og-url" .}}https://ridgwaysystems.org{{end}}">
|
||||
<!-- Twitter/X card -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="{{block "tw-title" .}}Ridgway Systems{{end}}">
|
||||
<meta name="twitter:description" content="{{block "tw-desc" .}}A homelab built on OpenBSD — from firewall to git server.{{end}}">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="stylesheet" href="/static/css/syntax.css">
|
||||
<link rel="alternate" type="application/rss+xml" title="Ridgway Systems" href="/blog/feed.xml">
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="nav">
|
||||
<a href="/" class="nav-brand">ridgwaysystems.org</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/blog">blog</a></li>
|
||||
<li><a href="/infrastructure">infrastructure</a></li>
|
||||
<li><a href="/status">status</a></li>
|
||||
<li><a href="/about">about</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
{{block "content" .}}{{end}}
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<p>
|
||||
<a href="/">ridgwaysystems.org</a> —
|
||||
running OpenBSD —
|
||||
<a href="/blog/feed.xml">RSS</a> —
|
||||
<a href="https://git.ridgwaysystems.org">gitea</a>
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
67
templates/blog.html
Normal file
67
templates/blog.html
Normal file
@@ -0,0 +1,67 @@
|
||||
{{define "title"}}Build Log — Ridgway Systems{{end}}
|
||||
{{define "meta-desc"}}OpenBSD homelab build log — documenting decisions, problems, and solutions.{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="page-header">
|
||||
<h1>Build Log</h1>
|
||||
<p class="page-desc">Documenting the OpenBSD homelab migration: what was built, how, and why.</p>
|
||||
</div>
|
||||
|
||||
<div class="blog-controls">
|
||||
<form action="/blog" method="GET" class="search-form" role="search">
|
||||
{{if .ActiveTag}}<input type="hidden" name="tag" value="{{.ActiveTag}}">{{end}}
|
||||
<input type="search" name="q" value="{{.SearchQuery}}" placeholder="Search posts…" aria-label="Search posts">
|
||||
<button type="submit" class="btn btn-sm">Search</button>
|
||||
{{if .SearchQuery}}<a href="/blog{{if .ActiveTag}}?tag={{.ActiveTag}}{{end}}" class="btn btn-sm btn-outline">Clear</a>{{end}}
|
||||
</form>
|
||||
|
||||
{{if .Tags}}
|
||||
<div class="tag-filter">
|
||||
<span class="tag-filter-label">filter:</span>
|
||||
<a href="/blog{{if .SearchQuery}}?q={{.SearchQuery}}{{end}}" class="tag{{if eq .ActiveTag ""}} tag-active{{end}}">#all</a>
|
||||
{{range .Tags}}
|
||||
<a href="/blog?tag={{.}}{{if $.SearchQuery}}&q={{$.SearchQuery}}{{end}}" class="tag{{if eq . $.ActiveTag}} tag-active{{end}}">#{{.}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if and .SearchQuery (not .Posts)}}
|
||||
<p class="empty-state">No results for “{{.SearchQuery}}”. <a href="/blog">Clear search.</a></p>
|
||||
{{else if .Posts}}
|
||||
<ul class="post-list post-list-full">
|
||||
{{range .Posts}}
|
||||
<li class="post-item">
|
||||
<div class="post-meta">
|
||||
<span class="post-date">{{formatDate .ParsedDate}}</span>
|
||||
{{if .Draft}}<span class="draft-badge">draft</span>{{end}}
|
||||
</div>
|
||||
<a href="/blog/{{.Slug}}" class="post-title">{{.Title}}</a>
|
||||
{{if .Description}}<p class="post-desc">{{.Description}}</p>{{end}}
|
||||
{{if .Tags}}
|
||||
<span class="post-tags">
|
||||
{{range .Tags}}<a href="/blog?tag={{.}}" class="tag">#{{.}}</a> {{end}}
|
||||
</span>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{if gt .TotalPages 1}}
|
||||
<nav class="pagination" aria-label="Page navigation">
|
||||
{{if .HasPrev}}
|
||||
<a href="/blog?page={{.PrevPage}}{{if .ActiveTag}}&tag={{.ActiveTag}}{{end}}{{if .SearchQuery}}&q={{.SearchQuery}}{{end}}" class="btn btn-outline btn-sm">← Newer</a>
|
||||
{{end}}
|
||||
<span class="page-indicator">page {{.Page}} of {{.TotalPages}}</span>
|
||||
{{if .HasNext}}
|
||||
<a href="/blog?page={{.NextPage}}{{if .ActiveTag}}&tag={{.ActiveTag}}{{end}}{{if .SearchQuery}}&q={{.SearchQuery}}{{end}}" class="btn btn-outline btn-sm">Older →</a>
|
||||
{{end}}
|
||||
</nav>
|
||||
{{end}}
|
||||
|
||||
{{else}}
|
||||
<p class="empty-state">No posts yet.
|
||||
{{if .ActiveTag}}Try <a href="/blog">removing the filter</a>.{{end}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
63
templates/index.html
Normal file
63
templates/index.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{{define "title"}}Ridgway Systems{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<section class="hero">
|
||||
<h1>Ridgway Systems</h1>
|
||||
<p class="tagline">A homelab built on OpenBSD — from firewall to git server.</p>
|
||||
<p class="hero-desc">
|
||||
A self-hosted infrastructure project running entirely on OpenBSD. This site documents the build:
|
||||
hardware decisions, network configuration, service deployments, and everything learned along the way.
|
||||
</p>
|
||||
<div class="hero-links">
|
||||
<a href="/blog" class="btn">build log</a>
|
||||
<a href="/infrastructure" class="btn btn-outline">infrastructure</a>
|
||||
<a href="/status" class="btn btn-outline">status</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="infra-summary">
|
||||
<h2>What's Running</h2>
|
||||
<div class="infra-grid">
|
||||
<div class="infra-card">
|
||||
<div class="infra-host">SuperMicro 1U</div>
|
||||
<div class="infra-role">Firewall • Router • VPN • Reverse Proxy</div>
|
||||
<div class="infra-detail">OpenBSD • pf • relayd • WireGuard</div>
|
||||
</div>
|
||||
<div class="infra-card">
|
||||
<div class="infra-host">Dell R720</div>
|
||||
<div class="infra-role">Primary Server</div>
|
||||
<div class="infra-detail">Gitea • Web • Mail • Monitoring • Chat</div>
|
||||
</div>
|
||||
<div class="infra-card">
|
||||
<div class="infra-host">Dell R710</div>
|
||||
<div class="infra-role">Backup • Game Servers</div>
|
||||
<div class="infra-detail">DNS • Linux VMs • Secondary services</div>
|
||||
</div>
|
||||
<div class="infra-card">
|
||||
<div class="infra-host">Desktop</div>
|
||||
<div class="infra-role">Daily Driver • Ansible Control</div>
|
||||
<div class="infra-detail">Development • Playbook management</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{if .RecentPosts}}
|
||||
<section class="recent-posts">
|
||||
<h2>Recent Posts</h2>
|
||||
<ul class="post-list">
|
||||
{{range .RecentPosts}}
|
||||
<li class="post-item">
|
||||
<span class="post-date">{{formatDate .ParsedDate}}</span>
|
||||
<a href="/blog/{{.Slug}}" class="post-title">{{.Title}}</a>
|
||||
{{if .Tags}}
|
||||
<span class="post-tags">
|
||||
{{range .Tags}}<a href="/blog?tag={{.}}" class="tag">#{{.}}</a> {{end}}
|
||||
</span>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
<a href="/blog" class="all-posts-link">All posts →</a>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
123
templates/infrastructure.html
Normal file
123
templates/infrastructure.html
Normal file
@@ -0,0 +1,123 @@
|
||||
{{define "title"}}Infrastructure — Ridgway Systems{{end}}
|
||||
{{define "meta-desc"}}Hardware inventory and network diagram for the Ridgway Systems OpenBSD homelab.{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="page-header">
|
||||
<h1>Infrastructure</h1>
|
||||
<p class="page-desc">Physical hardware, network layout, and service placement.</p>
|
||||
</div>
|
||||
|
||||
<section class="infra-section">
|
||||
<h2>Hardware</h2>
|
||||
<table class="hw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Host</th>
|
||||
<th>Hardware</th>
|
||||
<th>OS</th>
|
||||
<th>Role</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="hw-name">fw01</td>
|
||||
<td>SuperMicro 1U<br><span class="hw-spec">E3-1230v2 • 16 GB RAM</span></td>
|
||||
<td>OpenBSD</td>
|
||||
<td>Firewall, router, VPN, reverse proxy<br><span class="hw-spec">pf • relayd • WireGuard • unbound</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="hw-name">srv01</td>
|
||||
<td>Dell R720<br><span class="hw-spec">Xeon E5-2600 • 64 GB RAM</span></td>
|
||||
<td>OpenBSD</td>
|
||||
<td>Primary server<br><span class="hw-spec">Gitea • httpd • OpenSMTPD • Prometheus • Grafana • Matrix</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="hw-name">srv02</td>
|
||||
<td>Dell R710<br><span class="hw-spec">Xeon 5500/5600 • 48 GB RAM</span></td>
|
||||
<td>OpenBSD + Linux VMs</td>
|
||||
<td>Backup, game servers<br><span class="hw-spec">nsd • vmm • Jellyfin • secondary DNS</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="hw-name">ws01</td>
|
||||
<td>Desktop<br><span class="hw-spec">Ryzen • 32 GB RAM</span></td>
|
||||
<td>Linux</td>
|
||||
<td>Daily driver, Ansible control node<br><span class="hw-spec">Development • playbook management</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="infra-section">
|
||||
<h2>Network Diagram</h2>
|
||||
<pre class="network-diagram">
|
||||
Internet
|
||||
|
|
||||
[WAN interface]
|
||||
|
|
||||
+=================+
|
||||
| fw01 | SuperMicro 1U
|
||||
| OpenBSD | pf firewall
|
||||
| relayd | WireGuard VPN
|
||||
+=====+===========+
|
||||
|
|
||||
+-- [Management VLAN 1] -- fw01, switches, OOB
|
||||
|
|
||||
+-- [Servers VLAN 10] -- srv01, srv02
|
||||
| |
|
||||
| +-- srv01 (R720)
|
||||
| | httpd / relayd (external traffic routed here)
|
||||
| | Gitea, mail, monitoring, Matrix
|
||||
| |
|
||||
| +-- srv02 (R710)
|
||||
| DNS (nsd), Jellyfin, game VMs
|
||||
|
|
||||
+-- [Desktop VLAN 20] -- ws01, personal devices
|
||||
|
|
||||
+-- [Game VLAN 30] -- game clients, gaming VMs
|
||||
|
|
||||
+-- [IoT/Guest VLAN 40] -- untrusted devices
|
||||
|
||||
External traffic flow:
|
||||
Internet --> fw01 (relayd) --> srv01 (httpd/app)
|
||||
|
||||
VPN:
|
||||
WireGuard on fw01 --> routed to server VLANs
|
||||
</pre>
|
||||
</section>
|
||||
|
||||
<section class="infra-section">
|
||||
<h2>Services</h2>
|
||||
<table class="hw-table">
|
||||
<thead>
|
||||
<tr><th>Service</th><th>Host</th><th>URL</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Web / httpd</td><td>srv01</td><td>ridgwaysystems.org</td></tr>
|
||||
<tr><td>Gitea</td><td>srv01</td><td>git.ridgwaysystems.org</td></tr>
|
||||
<tr><td>Email (OpenSMTPD)</td><td>srv01</td><td>—</td></tr>
|
||||
<tr><td>DNS (unbound)</td><td>fw01</td><td>internal resolver</td></tr>
|
||||
<tr><td>DNS (nsd)</td><td>srv02</td><td>authoritative</td></tr>
|
||||
<tr><td>Prometheus + Grafana</td><td>srv01</td><td>monitoring.ridgwaysystems.org</td></tr>
|
||||
<tr><td>Matrix</td><td>srv01</td><td>matrix.ridgwaysystems.org</td></tr>
|
||||
<tr><td>Jellyfin</td><td>srv02</td><td>jellyfin.ridgwaysystems.org</td></tr>
|
||||
<tr><td>WireGuard VPN</td><td>fw01</td><td>vpn.ridgwaysystems.org</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="infra-section">
|
||||
<h2>VLAN Layout</h2>
|
||||
<table class="hw-table">
|
||||
<thead>
|
||||
<tr><th>VLAN</th><th>ID</th><th>Subnet</th><th>Purpose</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Management</td><td>1</td><td>10.0.1.0/24</td><td>Switches, OOB, firewall management</td></tr>
|
||||
<tr><td>Servers</td><td>10</td><td>10.0.10.0/24</td><td>srv01, srv02 — all hosted services</td></tr>
|
||||
<tr><td>Desktop</td><td>20</td><td>10.0.20.0/24</td><td>ws01 and personal devices</td></tr>
|
||||
<tr><td>Game</td><td>30</td><td>10.0.30.0/24</td><td>Gaming VMs and clients</td></tr>
|
||||
<tr><td>IoT/Guest</td><td>40</td><td>10.0.40.0/24</td><td>Untrusted / isolated devices</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{{end}}
|
||||
29
templates/post.html
Normal file
29
templates/post.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{{define "title"}}{{.Title}} — Ridgway Systems{{end}}
|
||||
{{define "meta-desc"}}{{.Description}}{{end}}
|
||||
{{define "og-title"}}{{.Title}} — Ridgway Systems{{end}}
|
||||
{{define "og-desc"}}{{.Description}}{{end}}
|
||||
{{define "og-type"}}article{{end}}
|
||||
{{define "og-url"}}https://ridgwaysystems.org/blog/{{.Slug}}{{end}}
|
||||
{{define "tw-title"}}{{.Title}} — Ridgway Systems{{end}}
|
||||
{{define "tw-desc"}}{{.Description}}{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<article class="post">
|
||||
<header class="post-header">
|
||||
<h1>{{.Title}}</h1>
|
||||
<div class="post-meta">
|
||||
<time datetime="{{.ParsedDate.Format "2006-01-02"}}">{{formatDate .ParsedDate}}</time>
|
||||
{{if .Tags}}
|
||||
—
|
||||
{{range .Tags}}<a href="/blog?tag={{.}}" class="tag">#{{.}}</a> {{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</header>
|
||||
<div class="post-content">
|
||||
{{.Content}}
|
||||
</div>
|
||||
<footer class="post-footer">
|
||||
<a href="/blog">← Back to build log</a>
|
||||
</footer>
|
||||
</article>
|
||||
{{end}}
|
||||
36
templates/status.html
Normal file
36
templates/status.html
Normal file
@@ -0,0 +1,36 @@
|
||||
{{define "title"}}Service Status — Ridgway Systems{{end}}
|
||||
{{define "meta-desc"}}Live status of services running on the Ridgway Systems homelab.{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="page-header">
|
||||
<h1>Service Status</h1>
|
||||
{{if .LastChecked}}
|
||||
<p class="page-desc">Last updated: <time>{{.LastChecked}}</time></p>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .Page.Services}}
|
||||
<ul class="status-list">
|
||||
{{range .Page.Services}}
|
||||
<li class="status-item status-{{.Status}}">
|
||||
<span class="status-indicator" aria-label="{{.Status}}"></span>
|
||||
<div class="status-info">
|
||||
<span class="status-name">{{.Name}}</span>
|
||||
{{if .Description}}<span class="status-desc">{{.Description}}</span>{{end}}
|
||||
{{if .Note}}<span class="status-note">{{.Note}}</span>{{end}}
|
||||
</div>
|
||||
<span class="status-badge status-badge-{{.Status}}">{{.Status}}</span>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p class="empty-state">No services configured.</p>
|
||||
{{end}}
|
||||
|
||||
<div class="status-legend">
|
||||
<span class="status-badge status-badge-up">up</span> operational
|
||||
<span class="status-badge status-badge-degraded">degraded</span> reduced capacity
|
||||
<span class="status-badge status-badge-down">down</span> unavailable
|
||||
<span class="status-badge status-badge-unknown">unknown</span> not checked
|
||||
</div>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user