@celilo/e2e 0.7.11 → 0.7.13-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.celilo-website-cache/_astro/docs.DwsUauWZ.css +1 -0
- package/.celilo-website-cache/_astro/network.BB0RR2mM.css +1 -0
- package/.celilo-website-cache/diagrams/physical.svg +1 -0
- package/.celilo-website-cache/diagrams/zone-topology.svg +1 -0
- package/.celilo-website-cache/docs/index.html +250 -0
- package/.celilo-website-cache/docs/network/index.html +300 -0
- package/.celilo-website-cache/favicon.svg +4 -0
- package/.celilo-website-cache/fonts/theia-300.otf +0 -0
- package/.celilo-website-cache/fonts/theia-400.otf +0 -0
- package/.celilo-website-cache/fonts/theia-500.otf +0 -0
- package/.celilo-website-cache/index.html +93 -0
- package/.celilo-website-cache/install.sh +330 -0
- package/.celilo-website-cache/modules/index.html +86 -0
- package/.celilo-website-cache/modules/view/index.html +69 -0
- package/.celilo-website-cache/vision/index.html +121 -0
- package/.npm-registry-cache/celilo-capabilities-0.1.10.tgz +0 -0
- package/.npm-registry-cache/celilo-cli-0.3.30.tgz +0 -0
- package/.npm-registry-cache/celilo-cli-display-0.1.9.tgz +0 -0
- package/.npm-registry-cache/celilo-e2e-0.7.13.tgz +0 -0
- package/.npm-registry-cache/celilo-event-bus-0.1.4.tgz +0 -0
- package/netapps/authentik.netapp +0 -0
- package/netapps/caddy.netapp +0 -0
- package/netapps/celilo-website.netapp +0 -0
- package/netapps/greenwave.netapp +0 -0
- package/netapps/homebridge.netapp +0 -0
- package/netapps/iptables.netapp +0 -0
- package/netapps/knot-unbound-internal.netapp +0 -0
- package/netapps/namecheap.netapp +0 -0
- package/netapps/technitium.netapp +0 -0
- package/package.json +6 -3
- package/src/docker-compose-generator.ts +30 -2
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-astro-cid-5hce7sga> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="Wire up your home lab for Celilo: VLANs, switch config, firewall, Proxmox bridges."><title>Network setup — Celilo</title><link rel="icon" type="image/svg+xml" href="/favicon.svg"><script>
|
|
2
|
+
// Resolve theme BEFORE first paint to avoid the flash of wrong
|
|
3
|
+
// theme. Order: explicit user choice (localStorage) → system
|
|
4
|
+
// preference (prefers-color-scheme) → fallback dark. The
|
|
5
|
+
// resolved value lands on <html data-theme="...">; CSS variables
|
|
6
|
+
// below pick the matching palette.
|
|
7
|
+
(function () {
|
|
8
|
+
try {
|
|
9
|
+
const stored = localStorage.getItem('celilo-theme');
|
|
10
|
+
const sys =
|
|
11
|
+
window.matchMedia &&
|
|
12
|
+
window.matchMedia('(prefers-color-scheme: light)').matches
|
|
13
|
+
? 'light'
|
|
14
|
+
: 'dark';
|
|
15
|
+
const theme = stored === 'light' || stored === 'dark' ? stored : sys;
|
|
16
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
17
|
+
} catch (_e) {
|
|
18
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
</script><link rel="stylesheet" href="/_astro/docs.DwsUauWZ.css">
|
|
22
|
+
<link rel="stylesheet" href="/_astro/network.BB0RR2mM.css"></head> <body data-astro-cid-5hce7sga> <!--
|
|
23
|
+
Background pattern — vertical streams of falling water drawn in
|
|
24
|
+
the geometric vocabulary of a Greek meander. The <pattern>
|
|
25
|
+
tile (240×240) repeats seamlessly across the viewport. Strokes
|
|
26
|
+
use `currentColor` so the .bg-pattern CSS rule above can tint
|
|
27
|
+
them via `color: var(--bg-pattern-color)` without rebuilding
|
|
28
|
+
the SVG. To change the streams themselves, edit the four <path>
|
|
29
|
+
elements below.
|
|
30
|
+
--> <svg class="bg-pattern" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-astro-cid-5hce7sga> <defs data-astro-cid-5hce7sga> <pattern id="celilo-streams" width="240" height="240" patternUnits="userSpaceOnUse" data-astro-cid-5hce7sga> <!-- Streams flow downward — the patternTransform shifts the
|
|
31
|
+
pattern origin by one full tile (240px) over `dur`, then
|
|
32
|
+
loops seamlessly. Edit `dur` for speed; delete this
|
|
33
|
+
<animateTransform> to freeze the flow. --> <animateTransform attributeName="patternTransform" type="translate" from="0 0" to="0 240" dur="30s" repeatCount="indefinite" data-astro-cid-5hce7sga></animateTransform> <g fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" data-astro-cid-5hce7sga> <!-- Stroke color cycle — driven by SMIL inside the SVG
|
|
34
|
+
(not CSS `color`) because Chromium doesn't reliably
|
|
35
|
+
repaint strokes that resolve via currentColor when
|
|
36
|
+
the parent's CSS `color` is animated. Edit the
|
|
37
|
+
`values` list to pick your palette; semicolons
|
|
38
|
+
separate stops, last value should match first to
|
|
39
|
+
close the loop seamlessly. --> <animate attributeName="stroke" values="#818cf8;#f472b6;#4ade80;#22d3ee;#818cf8" dur="10s" repeatCount="indefinite" data-astro-cid-5hce7sga></animate> <!-- Stream A (x=40): big staircase cascade near top, single hook near bottom --> <path d="M 40,0 V 30 H 55 V 45 H 70 V 60 H 55 V 75 H 40 V 175 H 25 V 195 H 40 V 240" data-astro-cid-5hce7sga></path> <!-- Stream B (x=100): small step near top, three-tier cascade in middle --> <path d="M 100,0 V 60 H 85 V 80 H 100 V 130 H 115 V 145 H 130 V 160 H 115 V 175 H 100 V 240" data-astro-cid-5hce7sga></path> <!-- Stream C (x=160): three small hooks distributed top-to-bottom --> <path d="M 160,0 V 20 H 175 V 35 H 160 V 110 H 145 V 130 H 160 V 200 H 175 V 220 H 160 V 240" data-astro-cid-5hce7sga></path> <!-- Stream D (x=220): single deep cascade in the upper-middle --> <path d="M 220,0 V 85 H 205 V 100 H 190 V 115 H 220 V 240" data-astro-cid-5hce7sga></path> </g> </pattern> </defs> <rect width="100%" height="100%" fill="url(#celilo-streams)" data-astro-cid-5hce7sga></rect> </svg> <nav data-astro-cid-5hce7sga> <div class="nav-inner" data-astro-cid-5hce7sga> <a href="/" class="nav-logo" data-astro-cid-5hce7sga>celilo</a> <ul class="nav-links" data-astro-cid-5hce7sga> <li data-astro-cid-5hce7sga> <a href="/docs" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M4 5a2 2 0 0 1 2-2h11v18H6a2 2 0 0 1-2-2V5z" data-astro-cid-5hce7sga></path> <path d="M9 7h6M9 11h6M9 15h4" data-astro-cid-5hce7sga></path> </svg>
|
|
40
|
+
docs
|
|
41
|
+
</a> </li> <li data-astro-cid-5hce7sga> <a href="/modules" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <rect x="3" y="3" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> <rect x="14" y="3" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> <rect x="3" y="14" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> <rect x="14" y="14" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> </svg>
|
|
42
|
+
modules
|
|
43
|
+
</a> </li> <li data-astro-cid-5hce7sga> <a href="/vision" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z" data-astro-cid-5hce7sga></path> <circle cx="12" cy="12" r="3" data-astro-cid-5hce7sga></circle> </svg>
|
|
44
|
+
vision
|
|
45
|
+
</a> </li> <li data-astro-cid-5hce7sga> <a href="https://github.com/celilo/celilo" target="_blank" rel="noopener" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M9 19c-4.3 1.4-4.3-2.5-6-3m12 5v-3.5c0-1 .1-1.4-.5-2 2.8-.3 5.5-1.4 5.5-6a4.6 4.6 0 0 0-1.3-3.2 4.2 4.2 0 0 0-.1-3.2s-1.1-.3-3.5 1.3a12.3 12.3 0 0 0-6.2 0C6.5 2.8 5.4 3.1 5.4 3.1a4.2 4.2 0 0 0-.1 3.2A4.6 4.6 0 0 0 4 9.5c0 4.6 2.7 5.7 5.5 6-.6.6-.6 1.2-.5 2V21" data-astro-cid-5hce7sga></path> </svg>
|
|
46
|
+
github
|
|
47
|
+
</a> </li> <li data-astro-cid-5hce7sga> <button type="button" class="theme-toggle" id="theme-toggle" aria-label="Toggle light/dark theme" title="Toggle theme" data-astro-cid-5hce7sga> <svg class="icon-sun" viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <circle cx="12" cy="12" r="4" data-astro-cid-5hce7sga></circle> <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" data-astro-cid-5hce7sga></path> </svg> <svg class="icon-moon" viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" data-astro-cid-5hce7sga></path> </svg> </button> </li> </ul> </div> </nav> <script>
|
|
48
|
+
// Click handler for the toggle. Reads the current data-theme,
|
|
49
|
+
// flips it, persists to localStorage, applies to <html>. The
|
|
50
|
+
// resolution script in <head> already set the initial value
|
|
51
|
+
// (system-aware) before paint, so this only runs on user
|
|
52
|
+
// interaction.
|
|
53
|
+
(function () {
|
|
54
|
+
const btn = document.getElementById('theme-toggle');
|
|
55
|
+
if (!btn) return;
|
|
56
|
+
btn.addEventListener('click', function () {
|
|
57
|
+
const current = document.documentElement.getAttribute('data-theme');
|
|
58
|
+
const next = current === 'light' ? 'dark' : 'light';
|
|
59
|
+
document.documentElement.setAttribute('data-theme', next);
|
|
60
|
+
try {
|
|
61
|
+
localStorage.setItem('celilo-theme', next);
|
|
62
|
+
} catch (_e) {
|
|
63
|
+
// localStorage unavailable (private mode) — toggle still
|
|
64
|
+
// works for the session, just doesn't persist.
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
})();
|
|
68
|
+
</script> <main data-astro-cid-5hce7sga> <nav class="breadcrumb" aria-label="Breadcrumb" data-astro-cid-ppabjpgh> <a href="/docs" data-astro-cid-ppabjpgh>Docs</a> <span aria-hidden="true" data-astro-cid-ppabjpgh>/</span> <span data-astro-cid-ppabjpgh>Network setup</span> </nav> <div class="page-header" data-astro-cid-ppabjpgh> <h1 data-astro-cid-ppabjpgh>Network setup</h1> <p data-astro-cid-ppabjpgh>Wire up your home lab so zones mean something. VLANs, switch, firewall, Proxmox bridges.</p> </div> <div class="docs-layout" data-astro-cid-ppabjpgh> <nav data-astro-cid-ppabjpgh> <ul class="docs-nav" data-astro-cid-ppabjpgh> <li class="nav-section" data-astro-cid-ppabjpgh>On this page</li> <li data-astro-cid-ppabjpgh><a href="#why-vlans" data-astro-cid-ppabjpgh>Why VLANs</a></li> <li data-astro-cid-ppabjpgh><a href="#topology" data-astro-cid-ppabjpgh>Reference topology</a></li> <li data-astro-cid-ppabjpgh><a href="#physical" data-astro-cid-ppabjpgh>Physical layout</a></li> <li data-astro-cid-ppabjpgh><a href="#recommended" data-astro-cid-ppabjpgh>Recommended: managed switch</a></li> <li data-astro-cid-ppabjpgh><a href="#minimal" data-astro-cid-ppabjpgh>Minimal: no switch</a></li> <li data-astro-cid-ppabjpgh><a href="#per-nic" data-astro-cid-ppabjpgh>Per-zone NICs</a></li> <li data-astro-cid-ppabjpgh><a href="#ingress" data-astro-cid-ppabjpgh>Perimeter ingress</a></li> <li data-astro-cid-ppabjpgh><a href="#wireless" data-astro-cid-ppabjpgh>Wireless</a></li> <li data-astro-cid-ppabjpgh><a href="#telling-celilo" data-astro-cid-ppabjpgh>Telling Celilo</a></li> </ul> </nav> <article class="docs-content" data-astro-cid-ppabjpgh> <p data-astro-cid-ppabjpgh>
|
|
69
|
+
Celilo's <a href="/docs#zones" data-astro-cid-ppabjpgh>zones</a> describe what's allowed to talk to what.
|
|
70
|
+
The firewall enforces those rules — but the firewall can only enforce them if
|
|
71
|
+
traffic between zones is forced to go <em data-astro-cid-ppabjpgh>through</em> it. Without that, zones are
|
|
72
|
+
honor-system: a container in <code data-astro-cid-ppabjpgh>app</code> that knows the gateway IP for
|
|
73
|
+
<code data-astro-cid-ppabjpgh>secure</code> can ARP for the database directly, the switch happily floods
|
|
74
|
+
the frame, and your "isolation" is a sticker on the box.
|
|
75
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
76
|
+
802.1q VLAN tagging is what makes zones real. This page walks through how to
|
|
77
|
+
wire your home lab so Celilo's policy actually has teeth.
|
|
78
|
+
</p> <h2 id="why-vlans" data-astro-cid-ppabjpgh>Why VLANs</h2> <p data-astro-cid-ppabjpgh>
|
|
79
|
+
Most home networks live on a single broadcast domain — one subnet, one switch,
|
|
80
|
+
every device's ARP requests reach every other device. That's fine for a flat
|
|
81
|
+
network, but it makes every "zone" the same zone. Anything talking to anything,
|
|
82
|
+
with the firewall as a polite suggestion.
|
|
83
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
84
|
+
802.1q tags every Ethernet frame with the VLAN it belongs to. The switch refuses
|
|
85
|
+
to forward frames between tags, so each zone is its own broadcast domain. A host
|
|
86
|
+
on the <code data-astro-cid-ppabjpgh>app</code> VLAN can send packets toward an IP on the
|
|
87
|
+
<code data-astro-cid-ppabjpgh>secure</code> VLAN, but those packets only get there if a router decides
|
|
88
|
+
to route them — and the only router with interfaces in both VLANs is your
|
|
89
|
+
firewall machine. That's where Celilo's policy lives.
|
|
90
|
+
</p> <p data-astro-cid-ppabjpgh>The practical upshot:</p> <ul data-astro-cid-ppabjpgh> <li data-astro-cid-ppabjpgh>Compromising a workload in <code data-astro-cid-ppabjpgh>app</code> doesn't get you L2 access to
|
|
91
|
+
<code data-astro-cid-ppabjpgh>secure</code>; you have to defeat the firewall, not just guess an IP.</li> <li data-astro-cid-ppabjpgh>You can put non-Celilo devices on a zone (a NAS in <code data-astro-cid-ppabjpgh>secure</code>, an
|
|
92
|
+
IoT camera in <code data-astro-cid-ppabjpgh>internal</code>) and they get the same policy enforcement
|
|
93
|
+
as Celilo-managed containers.</li> <li data-astro-cid-ppabjpgh>Adding a zone is a switch-config change plus a firewall-config change, not
|
|
94
|
+
re-cabling.</li> </ul> <h2 id="topology" data-astro-cid-ppabjpgh>Reference topology</h2> <p data-astro-cid-ppabjpgh>Here's how the zones, hosts, and capabilities fit together at the logical level:</p> <figure class="diagram" data-astro-cid-ppabjpgh> <img src="/diagrams/zone-topology.svg" alt="Celilo zone topology: external cloud connects through a perimeter firewall to internal (VLAN 192), where the celilo orchestrator lives; the iptables firewall connects internal to dmz (VLAN 10, hosting caddy with the public_web capability), app (VLAN 20, hosting authentik with the idp capability), and secure (VLAN 30, hosting postgres)." data-astro-cid-ppabjpgh> </figure> <p data-astro-cid-ppabjpgh>The five zones, with the example modules placed on each:</p> <ul data-astro-cid-ppabjpgh> <li data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>external</code> — the public internet. Untagged, lives upstream of the firewall. The cloud at the top of the diagram.</li> <li data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>internal</code> — VLAN 192 by default. Where you, the operator, and (today) the Celilo orchestrator live.</li> <li data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>dmz</code> — VLAN 10 by default. Public-facing workloads. Caddy lives here, terminating HTTPS.</li> <li data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>app</code> — VLAN 20 by default. Internal services. Authentik (the identity provider) lives here, as do most application servers.</li> <li data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>secure</code> — VLAN 30 by default. Sensitive data. Postgres lives here.</li> </ul> <p data-astro-cid-ppabjpgh>Two firewalls do different jobs:</p> <ul data-astro-cid-ppabjpgh> <li data-astro-cid-ppabjpgh>The <strong data-astro-cid-ppabjpgh>perimeter firewall</strong> (labeled <code data-astro-cid-ppabjpgh>perimeter-fw</code> in the diagram) is the only thing facing the public internet. It does NAT for outbound traffic, DNAT for inbound traffic on ports 80/443, and provides the <code data-astro-cid-ppabjpgh>firewall</code> capability that public-facing modules (Caddy, anything wanting <code data-astro-cid-ppabjpgh>exposeService</code>) call into. In your setup it can be a port-forward on your ISP router, a Cloudflare tunnel, or a VPS — see <a href="#ingress" data-astro-cid-ppabjpgh>Perimeter ingress</a>.</li> <li data-astro-cid-ppabjpgh><strong data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>iptables</code></strong> sits between zones. It's the inter-zone gateway: every packet from <code data-astro-cid-ppabjpgh>app</code> to <code data-astro-cid-ppabjpgh>secure</code>, or from <code data-astro-cid-ppabjpgh>dmz</code> to <code data-astro-cid-ppabjpgh>app</code>, passes through it and gets policy applied. Also provides the <code data-astro-cid-ppabjpgh>firewall</code> capability — modules don't have to know which firewall is which; they ask for <code data-astro-cid-ppabjpgh>firewall</code> and Celilo wires them to whichever provider serves their zone.</li> </ul> <p data-astro-cid-ppabjpgh>
|
|
95
|
+
The capability labels on the diagram (<code data-astro-cid-ppabjpgh>firewall</code>, <code data-astro-cid-ppabjpgh>idp</code>,
|
|
96
|
+
<code data-astro-cid-ppabjpgh>public_web</code>) are how modules find each other. Caddy declares it
|
|
97
|
+
provides <code data-astro-cid-ppabjpgh>public_web</code>; your web app's manifest declares it
|
|
98
|
+
<em data-astro-cid-ppabjpgh>requires</em> <code data-astro-cid-ppabjpgh>public_web</code>; Celilo wires the call without anyone
|
|
99
|
+
hardcoding hostnames or IPs. Same for <code data-astro-cid-ppabjpgh>idp</code> (Authentik) and
|
|
100
|
+
<code data-astro-cid-ppabjpgh>firewall</code> (whichever module provides the perimeter or inter-zone
|
|
101
|
+
firewall in your setup).
|
|
102
|
+
</p> <h2 id="physical" data-astro-cid-ppabjpgh>Physical layout</h2> <p data-astro-cid-ppabjpgh>
|
|
103
|
+
That's the logical view. Here's what the actual wiring looks like in a typical
|
|
104
|
+
deployment:
|
|
105
|
+
</p> <figure class="diagram" data-astro-cid-ppabjpgh> <img src="/diagrams/physical.svg" alt="Physical layout: ISP router with wifi hosts laptops, TVs, phones, IoT devices on a flat untrusted network. The firewall machine sits behind the ISP router with a single trunk port to a managed switch. The switch fans out: an access port to the management box and trunk ports to each of several Proxmox hosts." data-astro-cid-ppabjpgh> </figure> <p data-astro-cid-ppabjpgh>Five kinds of physical things:</p> <ul data-astro-cid-ppabjpgh> <li data-astro-cid-ppabjpgh><strong data-astro-cid-ppabjpgh>ISP router with wifi.</strong> Stays as you have it. This is where untrusted endpoints (laptops, phones, smart TVs, IoT) live. It's not <em data-astro-cid-ppabjpgh>part</em> of the Celilo network; it's the public side of the firewall.</li> <li data-astro-cid-ppabjpgh><strong data-astro-cid-ppabjpgh>Firewall machine.</strong> A small Linux box (Raspberry Pi, mini-PC, repurposed thin client) with two interfaces: one to the ISP router (untrusted, public side), one to the managed switch (trusted, trunk port carrying every VLAN). Runs the <code data-astro-cid-ppabjpgh>iptables</code> module to provide inter-zone firewalling. If you choose to also run a perimeter firewall locally (option A in <a href="#ingress" data-astro-cid-ppabjpgh>Perimeter ingress</a>), the same box can do both jobs — or you can split them onto separate hosts.</li> <li data-astro-cid-ppabjpgh><strong data-astro-cid-ppabjpgh>Managed switch.</strong> Any switch that supports 802.1q. Trunk port to the firewall, trunk port to each Proxmox host, access port to anything else (mgmt box, NAS, wired endpoints).</li> <li data-astro-cid-ppabjpgh><strong data-astro-cid-ppabjpgh>Proxmox hosts.</strong> One or more. Each connects to the switch over a single trunk port; <code data-astro-cid-ppabjpgh>vmbr0</code> is configured VLAN-aware, and containers attach with a <code data-astro-cid-ppabjpgh>tag=N</code> matching their zone.</li> <li data-astro-cid-ppabjpgh><strong data-astro-cid-ppabjpgh>Mgmt box.</strong> Whatever machine you run <code data-astro-cid-ppabjpgh>celilo</code> from. Usually on an access port carrying the <code data-astro-cid-ppabjpgh>internal</code> VLAN untagged.</li> </ul> <h2 id="recommended" data-astro-cid-ppabjpgh>Recommended: managed switch with 802.1q</h2> <p data-astro-cid-ppabjpgh>This is the layout above. Three things to configure:</p> <h3 data-astro-cid-ppabjpgh>Pick your numbers</h3> <p data-astro-cid-ppabjpgh>Celilo ships with these defaults, which match the diagrams above:</p> <table data-astro-cid-ppabjpgh> <thead data-astro-cid-ppabjpgh> <tr data-astro-cid-ppabjpgh><th data-astro-cid-ppabjpgh>Zone</th><th data-astro-cid-ppabjpgh>VLAN</th><th data-astro-cid-ppabjpgh>Default subnet</th><th data-astro-cid-ppabjpgh>Notes</th></tr> </thead> <tbody data-astro-cid-ppabjpgh> <tr data-astro-cid-ppabjpgh><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>dmz</code></td><td data-astro-cid-ppabjpgh>10</td><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>10.0.10.0/24</code></td><td data-astro-cid-ppabjpgh>Public-facing workloads (Caddy)</td></tr> <tr data-astro-cid-ppabjpgh><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>app</code></td><td data-astro-cid-ppabjpgh>20</td><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>10.0.20.0/24</code></td><td data-astro-cid-ppabjpgh>Internal services (Authentik, internal APIs)</td></tr> <tr data-astro-cid-ppabjpgh><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>secure</code></td><td data-astro-cid-ppabjpgh>30</td><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>10.0.30.0/24</code></td><td data-astro-cid-ppabjpgh>Databases, secret stores</td></tr> <tr data-astro-cid-ppabjpgh><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>internal</code></td><td data-astro-cid-ppabjpgh>192</td><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>192.168.1.0/24</code></td><td data-astro-cid-ppabjpgh>Operator LAN, mgmt</td></tr> <tr data-astro-cid-ppabjpgh><td data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>external</code></td><td data-astro-cid-ppabjpgh>—</td><td data-astro-cid-ppabjpgh>—</td><td data-astro-cid-ppabjpgh>Off-LAN; lives on the public side of the firewall</td></tr> </tbody> </table> <p data-astro-cid-ppabjpgh>If you have to use different VLAN numbers (existing network, conflicts with a vendor default), you can override them later — see <a href="#telling-celilo" data-astro-cid-ppabjpgh>Telling Celilo about it</a>.</p> <h3 data-astro-cid-ppabjpgh>Switch configuration</h3> <p data-astro-cid-ppabjpgh>Vendor specifics vary; the shape is the same:</p> <ul data-astro-cid-ppabjpgh> <li data-astro-cid-ppabjpgh>Create VLANs 10, 20, 30, 192 in the switch's admin UI.</li> <li data-astro-cid-ppabjpgh>The port to the firewall machine: <strong data-astro-cid-ppabjpgh>tagged trunk</strong> — all four VLANs tagged. (Some vendors call this "general" or "hybrid"; the point is the firewall sees the tag.)</li> <li data-astro-cid-ppabjpgh>Each port to a Proxmox host: <strong data-astro-cid-ppabjpgh>tagged trunk</strong> — same four VLANs tagged.</li> <li data-astro-cid-ppabjpgh>Port to the mgmt box: <strong data-astro-cid-ppabjpgh>access port, untagged on VLAN 192</strong>.</li> <li data-astro-cid-ppabjpgh>Ports for wired endpoints (printers, NAS, anything not running zone-aware software): <strong data-astro-cid-ppabjpgh>access port</strong>, untagged on whichever VLAN that device should live on.</li> </ul> <p data-astro-cid-ppabjpgh>
|
|
106
|
+
For step-by-step admin-UI walkthroughs, the vendor's docs are the source of
|
|
107
|
+
truth — UniFi, MikroTik, TP-Link/Omada, Cisco, and Netgear all have working
|
|
108
|
+
guides. The Celilo side doesn't care which switch you use, only that the tags
|
|
109
|
+
match the numbers above.
|
|
110
|
+
</p> <div class="callout" data-astro-cid-ppabjpgh> <strong data-astro-cid-ppabjpgh>Hardware suggestion:</strong> the
|
|
111
|
+
<a href="https://www.sodola-network.com/products/sodola-9-port-2-5g-smart-web-ethernet-switch-1-10g-sfp-slot-8-x-2-5g-base-t-ports-static-aggregation-qos-vlan-igmp-supported-metal-fanless-managed-multi-gigabit-switch-mtwg" target="_blank" rel="noopener" data-astro-cid-ppabjpgh>Sodola 9-port 2.5G smart switch</a>
|
|
112
|
+
(8× 2.5G + 1× 10G SFP+, fanless, ~$100) is a known-working option that's been
|
|
113
|
+
used in real Celilo deployments. If that link rots, search for "Sodola 9-port
|
|
114
|
+
2.5G smart managed switch" — same shape, plenty of stock.
|
|
115
|
+
</div> <h3 data-astro-cid-ppabjpgh>Firewall machine</h3> <p data-astro-cid-ppabjpgh>
|
|
116
|
+
Celilo's <code data-astro-cid-ppabjpgh>iptables</code> module configures the firewall rules for you.
|
|
117
|
+
What you have to set up <em data-astro-cid-ppabjpgh>before</em> running Celilo is the VLAN sub-interfaces
|
|
118
|
+
themselves, so the kernel has somewhere to apply rules. On Debian/Ubuntu,
|
|
119
|
+
<code data-astro-cid-ppabjpgh>/etc/network/interfaces</code> looks like:
|
|
120
|
+
</p> <pre data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>auto eth0
|
|
121
|
+
iface eth0 inet manual
|
|
122
|
+
# Trunk to the switch — no IP on the trunk itself
|
|
123
|
+
|
|
124
|
+
auto eth0.10
|
|
125
|
+
iface eth0.10 inet static
|
|
126
|
+
address 10.0.10.1/24
|
|
127
|
+
|
|
128
|
+
auto eth0.20
|
|
129
|
+
iface eth0.20 inet static
|
|
130
|
+
address 10.0.20.1/24
|
|
131
|
+
|
|
132
|
+
auto eth0.30
|
|
133
|
+
iface eth0.30 inet static
|
|
134
|
+
address 10.0.30.1/24
|
|
135
|
+
|
|
136
|
+
auto eth0.192
|
|
137
|
+
iface eth0.192 inet static
|
|
138
|
+
address 192.168.1.1/24
|
|
139
|
+
|
|
140
|
+
# eth1 is the public side facing the ISP router
|
|
141
|
+
auto eth1
|
|
142
|
+
iface eth1 inet dhcp</code></pre> <p data-astro-cid-ppabjpgh>
|
|
143
|
+
Each <code data-astro-cid-ppabjpgh>eth0.N</code> sub-interface is the gateway for its zone — that's the
|
|
144
|
+
IP Celilo's auto-derived <code data-astro-cid-ppabjpgh>gateway</code> field will produce when a module
|
|
145
|
+
asks for one. Restart networking, confirm <code data-astro-cid-ppabjpgh>ip -br addr</code> shows the
|
|
146
|
+
sub-interfaces up, then <code data-astro-cid-ppabjpgh>celilo machine add</code> the firewall and run
|
|
147
|
+
<code data-astro-cid-ppabjpgh>module deploy iptables</code> to lay down the rules.
|
|
148
|
+
</p> <h3 data-astro-cid-ppabjpgh>Proxmox host</h3> <p data-astro-cid-ppabjpgh>
|
|
149
|
+
Each Proxmox host needs a single VLAN-aware bridge. <code data-astro-cid-ppabjpgh>/etc/network/interfaces</code>:
|
|
150
|
+
</p> <pre data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>auto vmbr0
|
|
151
|
+
iface vmbr0 inet static
|
|
152
|
+
address 192.168.1.10/24
|
|
153
|
+
gateway 192.168.1.1
|
|
154
|
+
bridge-ports eno1
|
|
155
|
+
bridge-stp off
|
|
156
|
+
bridge-fd 0
|
|
157
|
+
bridge-vlan-aware yes
|
|
158
|
+
bridge-vids 2-4094</code></pre> <p data-astro-cid-ppabjpgh>
|
|
159
|
+
Two important bits: <code data-astro-cid-ppabjpgh>bridge-vlan-aware yes</code> tells the bridge to
|
|
160
|
+
respect 802.1q tags, and <code data-astro-cid-ppabjpgh>bridge-vids 2-4094</code> permits the full tag
|
|
161
|
+
range on the bridge (Proxmox restricts it by default). The Proxmox host itself
|
|
162
|
+
is reachable on the untagged segment of the bridge — most setups put that on
|
|
163
|
+
<code data-astro-cid-ppabjpgh>internal</code> so you can SSH in from your mgmt box.
|
|
164
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
165
|
+
Once the bridge is up, container creation is hands-off. Celilo's Terraform
|
|
166
|
+
templates emit a <code data-astro-cid-ppabjpgh>network</code> block with <code data-astro-cid-ppabjpgh>tag = $self:vlan</code>,
|
|
167
|
+
and Celilo auto-derives <code data-astro-cid-ppabjpgh>vlan</code> from the zone the module requested
|
|
168
|
+
— see <a href="#telling-celilo" data-astro-cid-ppabjpgh>Telling Celilo about it</a>.
|
|
169
|
+
</p> <h2 id="minimal" data-astro-cid-ppabjpgh>Minimal: firewall ↔ Proxmox direct, no switch</h2> <p data-astro-cid-ppabjpgh>
|
|
170
|
+
If you don't have a managed switch (yet), you can still get zone enforcement.
|
|
171
|
+
The trick is to skip the switch entirely and run a trunk straight from the
|
|
172
|
+
firewall to the Proxmox host. Two viable shapes:
|
|
173
|
+
</p> <h3 data-astro-cid-ppabjpgh>Shape A: single cable, sub-interfaces on both ends</h3> <p data-astro-cid-ppabjpgh>
|
|
174
|
+
One Ethernet cable from the firewall's "trusted" NIC to a Proxmox NIC. Both
|
|
175
|
+
ends speak 802.1q. Configuration is identical to the recommended setup above —
|
|
176
|
+
<code data-astro-cid-ppabjpgh>eth0.10/20/30/192</code> on the firewall, VLAN-aware <code data-astro-cid-ppabjpgh>vmbr0</code>
|
|
177
|
+
on Proxmox — you just don't have a switch in between.
|
|
178
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
179
|
+
What you give up: there's nowhere to plug a non-Proxmox device into a specific
|
|
180
|
+
zone. Want a NAS in <code data-astro-cid-ppabjpgh>secure</code>? It has to be a Proxmox container, or
|
|
181
|
+
you have to add a switch.
|
|
182
|
+
</p> <h3 data-astro-cid-ppabjpgh>Shape B: multi-NIC firewall, untagged per-zone subnets</h3> <p data-astro-cid-ppabjpgh>
|
|
183
|
+
If your firewall machine has several NICs, you can give each zone its own
|
|
184
|
+
untagged NIC and crossover-cable each one to a Proxmox NIC. No 802.1q anywhere.
|
|
185
|
+
Simpler to debug, but it requires Proxmox bridges per zone (<code data-astro-cid-ppabjpgh>vmbr10</code>,
|
|
186
|
+
<code data-astro-cid-ppabjpgh>vmbr20</code>, etc.) and the same NIC-port-shortage problem as
|
|
187
|
+
<a href="#per-nic" data-astro-cid-ppabjpgh>per-zone NICs</a> below.
|
|
188
|
+
</p> <div class="callout" data-astro-cid-ppabjpgh> <strong data-astro-cid-ppabjpgh>Status:</strong> Shape A is what most no-switch home labs end up with;
|
|
189
|
+
Celilo doesn't need anything different from the recommended setup. Shape B
|
|
190
|
+
works in principle but is less tested. If you're standing one of these up,
|
|
191
|
+
let us know how it goes — the doc page wants a real wiring photo.
|
|
192
|
+
</div> <h2 id="per-nic" data-astro-cid-ppabjpgh>Per-zone NICs (no VLANs)</h2> <p data-astro-cid-ppabjpgh>
|
|
193
|
+
Each zone gets its own physical NIC on the firewall and on each Proxmox host.
|
|
194
|
+
Cleanest L2 isolation, no tagging to misconfigure — but you run out of NIC
|
|
195
|
+
ports fast. Five zones means five NICs per Proxmox host, and most home-lab
|
|
196
|
+
boxes ship with one or two onboard.
|
|
197
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
198
|
+
Worth knowing exists; in practice 802.1q is what most home labs use because
|
|
199
|
+
it makes "add another zone" a config change rather than a hardware change.
|
|
200
|
+
Vendor docs (Proxmox networking, Linux bridge) cover the per-NIC setup.
|
|
201
|
+
</p> <h2 id="ingress" data-astro-cid-ppabjpgh>Perimeter ingress</h2> <p data-astro-cid-ppabjpgh>
|
|
202
|
+
The diagrams show a Linux box at the perimeter doing public NAT and inbound
|
|
203
|
+
DNAT for ports 80/443. That's one option, but it's not the only one. Three
|
|
204
|
+
patterns work today; pick whichever fits your ISP situation and tolerance for
|
|
205
|
+
outsourced trust:
|
|
206
|
+
</p> <h3 data-astro-cid-ppabjpgh>A. Port-forwarding on your ISP router</h3> <p data-astro-cid-ppabjpgh>
|
|
207
|
+
The simplest setup. Configure port-forward rules in your ISP router's admin
|
|
208
|
+
UI to send TCP 80/443 to your DMZ host (typically the Caddy container — its
|
|
209
|
+
IP is what <code data-astro-cid-ppabjpgh>module deploy caddy</code> prints on success). No extra
|
|
210
|
+
hardware, no public IP beyond what the ISP gives you. Most consumer routers
|
|
211
|
+
support this.
|
|
212
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
213
|
+
Recommended starting point. A planned <code data-astro-cid-ppabjpgh>isp-router</code> Celilo module
|
|
214
|
+
will give you a checklist of port-forward rules to enter manually and confirm
|
|
215
|
+
when done; until then, do it by hand once and forget about it.
|
|
216
|
+
</p> <h3 data-astro-cid-ppabjpgh>B. Cloudflare tunnel</h3> <p data-astro-cid-ppabjpgh>
|
|
217
|
+
No port-forward, no public IP needed.
|
|
218
|
+
<a href="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/" target="_blank" rel="noopener" data-astro-cid-ppabjpgh>Cloudflare Tunnel</a>
|
|
219
|
+
runs a daemon inside your network that dials out to Cloudflare's edge;
|
|
220
|
+
Cloudflare proxies inbound HTTPS through that tunnel back to your DMZ. Works
|
|
221
|
+
behind CG-NAT, works on residential connections that block inbound traffic.
|
|
222
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
223
|
+
Trade-offs: HTTPS terminates at Cloudflare (you trust them with TLS for the
|
|
224
|
+
domains you tunnel), and you're tied to their pricing/policy. Not yet wrapped
|
|
225
|
+
in a Celilo module; configure it manually, then point the tunnel's local
|
|
226
|
+
endpoint at Caddy.
|
|
227
|
+
</p> <h3 data-astro-cid-ppabjpgh>C. VPS + WireGuard</h3> <p data-astro-cid-ppabjpgh>
|
|
228
|
+
Rent a small public-IP VPS (Digital Ocean, Hetzner, Vultr — $4–$6/month),
|
|
229
|
+
run a WireGuard tunnel between it and a host on your <code data-astro-cid-ppabjpgh>dmz</code>, and
|
|
230
|
+
forward inbound 80/443 on the VPS through the tunnel into your network.
|
|
231
|
+
The VPS becomes your "public IP" for ingress purposes; TLS can terminate
|
|
232
|
+
either at the VPS or at home, your call.
|
|
233
|
+
</p> <p data-astro-cid-ppabjpgh>
|
|
234
|
+
Most flexible, most setup. Useful if you're behind CG-NAT, want a static
|
|
235
|
+
public IP, or want geographically-distant ingress. Not yet wrapped as a
|
|
236
|
+
Celilo module either; manual today.
|
|
237
|
+
</p> <div class="callout" data-astro-cid-ppabjpgh> <strong data-astro-cid-ppabjpgh>Today:</strong> none of A/B/C is automated by Celilo end-to-end. You
|
|
238
|
+
configure them manually, then Celilo's <code data-astro-cid-ppabjpgh>firewall</code> capability
|
|
239
|
+
operates inside the perimeter (DMZ container ↔ app ↔ secure). The
|
|
240
|
+
<code data-astro-cid-ppabjpgh>perimeter-fw</code> box in the topology diagram is a stand-in for
|
|
241
|
+
whichever of these you choose.
|
|
242
|
+
</div> <h2 id="wireless" data-astro-cid-ppabjpgh>Wireless</h2> <p data-astro-cid-ppabjpgh>
|
|
243
|
+
Wifi isn't part of the Celilo zone model. Connect to your ISP router's wifi
|
|
244
|
+
the way you always have — that's where laptops, phones, and IoT devices
|
|
245
|
+
live, and it's how you reach the orchestrator on <code data-astro-cid-ppabjpgh>internal</code>.
|
|
246
|
+
Celilo's protected zones (<code data-astro-cid-ppabjpgh>dmz</code>, <code data-astro-cid-ppabjpgh>app</code>,
|
|
247
|
+
<code data-astro-cid-ppabjpgh>secure</code>) are wired-only by design; there's no reason to want
|
|
248
|
+
clients on them over wifi.
|
|
249
|
+
</p> <h2 id="telling-celilo" data-astro-cid-ppabjpgh>Telling Celilo about it</h2> <p data-astro-cid-ppabjpgh>Two pieces of state Celilo cares about: <strong data-astro-cid-ppabjpgh>subnets</strong> and <strong data-astro-cid-ppabjpgh>VLAN tags</strong>.</p> <h3 data-astro-cid-ppabjpgh>Subnets</h3> <p data-astro-cid-ppabjpgh> <code data-astro-cid-ppabjpgh>celilo system init</code> prompts for each zone's subnet during initial
|
|
250
|
+
setup. Defaults match the table above. You can also set them non-interactively:
|
|
251
|
+
</p> <pre data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>celilo system config set network.dmz.subnet 10.0.10.0/24
|
|
252
|
+
celilo system config set network.app.subnet 10.0.20.0/24
|
|
253
|
+
celilo system config set network.secure.subnet 10.0.30.0/24
|
|
254
|
+
celilo system config set network.internal.subnet 192.168.1.0/24</code></pre> <p data-astro-cid-ppabjpgh>
|
|
255
|
+
Gateway IPs aren't prompted for. When you <code data-astro-cid-ppabjpgh>celilo machine add</code> the
|
|
256
|
+
firewall machine, Celilo SSHes in and inspects its interfaces; the IP it
|
|
257
|
+
finds on each VLAN sub-interface (<code data-astro-cid-ppabjpgh>eth0.10</code>, <code data-astro-cid-ppabjpgh>eth0.20</code>,
|
|
258
|
+
and so on) becomes the canonical gateway for that zone.
|
|
259
|
+
</p> <h3 data-astro-cid-ppabjpgh>VLAN tags</h3> <p data-astro-cid-ppabjpgh>
|
|
260
|
+
Celilo ships with VLAN tag defaults that match the diagrams above (<code data-astro-cid-ppabjpgh>dmz=10</code>,
|
|
261
|
+
<code data-astro-cid-ppabjpgh>app=20</code>, <code data-astro-cid-ppabjpgh>secure=30</code>, <code data-astro-cid-ppabjpgh>internal=192</code>). If your
|
|
262
|
+
switch uses different numbers — for example, a vendor default that conflicts —
|
|
263
|
+
override them:
|
|
264
|
+
</p> <pre data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>celilo system config set network.dmz.vlan 110
|
|
265
|
+
celilo system config set network.app.vlan 120
|
|
266
|
+
celilo system config set network.secure.vlan 130
|
|
267
|
+
celilo system config set network.internal.vlan 1</code></pre> <p data-astro-cid-ppabjpgh>
|
|
268
|
+
Whatever numbers you choose here have to match the tags on your switch and the
|
|
269
|
+
sub-interface names on the firewall (<code data-astro-cid-ppabjpgh>eth0.110</code> instead of
|
|
270
|
+
<code data-astro-cid-ppabjpgh>eth0.10</code>, etc.). They're the canonical record; modules pick up these
|
|
271
|
+
values automatically.
|
|
272
|
+
</p> <h3 data-astro-cid-ppabjpgh>What modules see</h3> <p data-astro-cid-ppabjpgh>
|
|
273
|
+
When a module requests an LXC container or a machine in a zone (e.g.
|
|
274
|
+
<code data-astro-cid-ppabjpgh>requires.machine.zone: app</code> in its manifest), Celilo auto-injects
|
|
275
|
+
the matching <code data-astro-cid-ppabjpgh>vlan</code>, <code data-astro-cid-ppabjpgh>gateway</code>, <code data-astro-cid-ppabjpgh>subnet</code>, and
|
|
276
|
+
<code data-astro-cid-ppabjpgh>bridge</code> values into that module's <code data-astro-cid-ppabjpgh>$self:</code> namespace.
|
|
277
|
+
The module's Terraform template
|
|
278
|
+
then drops them straight into the Proxmox container spec:
|
|
279
|
+
</p> <pre data-astro-cid-ppabjpgh><code data-astro-cid-ppabjpgh>resource "proxmox_lxc" "my_app" {
|
|
280
|
+
network {
|
|
281
|
+
name = "eth0"
|
|
282
|
+
bridge = "$self:bridge" # → vmbr0
|
|
283
|
+
tag = $self:vlan # → 20 (for the 'app' zone)
|
|
284
|
+
ip = "$self:target_ip"
|
|
285
|
+
gw = "$self:gateway" # → 10.0.20.1
|
|
286
|
+
}
|
|
287
|
+
}</code></pre> <p data-astro-cid-ppabjpgh>
|
|
288
|
+
You don't set per-module VLAN tags by hand. Declaring the zone is enough.
|
|
289
|
+
See <code data-astro-cid-ppabjpgh>apps/celilo/src/variables/context.ts</code> for the auto-derivation
|
|
290
|
+
logic if you want to dig in.
|
|
291
|
+
</p> <div class="callout" data-astro-cid-ppabjpgh> <strong data-astro-cid-ppabjpgh>Known limitation:</strong> <code data-astro-cid-ppabjpgh>celilo system init</code> prompts for
|
|
292
|
+
subnets but not VLAN tags. The defaults are usually right, but if you need
|
|
293
|
+
custom numbers you have to set them with <code data-astro-cid-ppabjpgh>system config set</code>
|
|
294
|
+
afterwards. Promoting VLAN tag prompts into the interactive init flow is a
|
|
295
|
+
likely improvement.
|
|
296
|
+
</div> <p data-astro-cid-ppabjpgh>
|
|
297
|
+
Once your network is wired and Celilo knows the numbers, you're done with this
|
|
298
|
+
page. Head back to <a href="/docs#proxmox" data-astro-cid-ppabjpgh>step 2 of the main walkthrough</a>
|
|
299
|
+
to add your Proxmox service.
|
|
300
|
+
</p> </article> </div> </main> <footer data-astro-cid-5hce7sga> <p data-astro-cid-5hce7sga>Celilo — home lab orchestration. Built with care.</p> </footer> </body></html>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-astro-cid-5hce7sga> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="Celilo — home lab orchestration. Deploy infrastructure the right way."><title>Home — Celilo</title><link rel="icon" type="image/svg+xml" href="/favicon.svg"><script>
|
|
2
|
+
// Resolve theme BEFORE first paint to avoid the flash of wrong
|
|
3
|
+
// theme. Order: explicit user choice (localStorage) → system
|
|
4
|
+
// preference (prefers-color-scheme) → fallback dark. The
|
|
5
|
+
// resolved value lands on <html data-theme="...">; CSS variables
|
|
6
|
+
// below pick the matching palette.
|
|
7
|
+
(function () {
|
|
8
|
+
try {
|
|
9
|
+
const stored = localStorage.getItem('celilo-theme');
|
|
10
|
+
const sys =
|
|
11
|
+
window.matchMedia &&
|
|
12
|
+
window.matchMedia('(prefers-color-scheme: light)').matches
|
|
13
|
+
? 'light'
|
|
14
|
+
: 'dark';
|
|
15
|
+
const theme = stored === 'light' || stored === 'dark' ? stored : sys;
|
|
16
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
17
|
+
} catch (_e) {
|
|
18
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
</script><link rel="stylesheet" href="/_astro/docs.DwsUauWZ.css">
|
|
22
|
+
<style>.hero[data-astro-cid-j7pv25f6]{padding:5rem 0 4rem;text-align:center}.hero-eyebrow[data-astro-cid-j7pv25f6]{font-family:var(--font-mono);font-size:.8rem;letter-spacing:.15em;text-transform:uppercase;color:var(--accent);margin-bottom:1.5rem}.hero[data-astro-cid-j7pv25f6] h1[data-astro-cid-j7pv25f6]{font-size:clamp(2.5rem,6vw,4.5rem);font-weight:800;letter-spacing:-.03em;margin-bottom:1.25rem;background:linear-gradient(135deg,var(--text) 0%,var(--accent-light) 60%,var(--accent) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.hero-subtitle[data-astro-cid-j7pv25f6]{font-size:clamp(1rem,2.5vw,1.25rem);color:var(--muted);max-width:40rem;margin:0 auto 2.5rem;line-height:1.7}.hero-actions[data-astro-cid-j7pv25f6]{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap}.btn[data-astro-cid-j7pv25f6]{display:inline-flex;align-items:center;gap:.5rem;padding:.75rem 1.75rem;border-radius:8px;font-size:.95rem;font-weight:500;transition:all .15s;cursor:pointer}.btn-primary[data-astro-cid-j7pv25f6]{background:var(--accent);color:#fff;border:1px solid transparent}.btn-primary[data-astro-cid-j7pv25f6]:hover{background:var(--accent-light);text-decoration:none}.btn-secondary[data-astro-cid-j7pv25f6]{background:transparent;color:var(--text);border:1px solid var(--border)}.btn-secondary[data-astro-cid-j7pv25f6]:hover{border-color:var(--muted);text-decoration:none}.terminal[data-astro-cid-j7pv25f6]{max-width:52rem;margin:4rem auto;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden}.terminal-bar[data-astro-cid-j7pv25f6]{background:color-mix(in srgb,var(--surface) 85%,var(--text) 5%);padding:.6rem 1rem;display:flex;align-items:center;gap:.5rem;border-bottom:1px solid var(--border)}.dot[data-astro-cid-j7pv25f6]{width:12px;height:12px;border-radius:50%}.dot-red[data-astro-cid-j7pv25f6]{background:#ff5f57}.dot-yellow[data-astro-cid-j7pv25f6]{background:#febc2e}.dot-green[data-astro-cid-j7pv25f6]{background:#28c840}.terminal-title[data-astro-cid-j7pv25f6]{font-family:var(--font-mono);font-size:.75rem;color:var(--muted);margin-left:auto;margin-right:auto}.terminal-body[data-astro-cid-j7pv25f6]{padding:1.5rem;font-family:var(--font-mono);font-size:.85rem;line-height:2}.cmd[data-astro-cid-j7pv25f6]{color:var(--green)}.cmd[data-astro-cid-j7pv25f6]:before{content:"$ ";color:var(--muted)}.out[data-astro-cid-j7pv25f6]{color:var(--muted)}.ok[data-astro-cid-j7pv25f6]{color:var(--green)}.features[data-astro-cid-j7pv25f6]{padding:4rem 0;display:grid;grid-template-columns:repeat(auto-fit,minmax(18rem,1fr));gap:1.5rem}.feature[data-astro-cid-j7pv25f6]{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:1.75rem;transition:border-color .15s}.feature[data-astro-cid-j7pv25f6]:hover{border-color:var(--accent)}.feature-icon[data-astro-cid-j7pv25f6]{font-size:1.75rem;margin-bottom:1rem}.feature[data-astro-cid-j7pv25f6] h3[data-astro-cid-j7pv25f6]{font-size:1.05rem;font-weight:600;margin-bottom:.5rem}.feature[data-astro-cid-j7pv25f6] p[data-astro-cid-j7pv25f6]{color:var(--muted);font-size:.9rem;line-height:1.6}.section-title[data-astro-cid-j7pv25f6]{font-size:1.75rem;font-weight:700;letter-spacing:-.02em;margin-bottom:.75rem}.section-sub[data-astro-cid-j7pv25f6]{color:var(--muted);font-size:.95rem;margin-bottom:2.5rem}
|
|
23
|
+
</style></head> <body data-astro-cid-5hce7sga> <!--
|
|
24
|
+
Background pattern — vertical streams of falling water drawn in
|
|
25
|
+
the geometric vocabulary of a Greek meander. The <pattern>
|
|
26
|
+
tile (240×240) repeats seamlessly across the viewport. Strokes
|
|
27
|
+
use `currentColor` so the .bg-pattern CSS rule above can tint
|
|
28
|
+
them via `color: var(--bg-pattern-color)` without rebuilding
|
|
29
|
+
the SVG. To change the streams themselves, edit the four <path>
|
|
30
|
+
elements below.
|
|
31
|
+
--> <svg class="bg-pattern" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" data-astro-cid-5hce7sga> <defs data-astro-cid-5hce7sga> <pattern id="celilo-streams" width="240" height="240" patternUnits="userSpaceOnUse" data-astro-cid-5hce7sga> <!-- Streams flow downward — the patternTransform shifts the
|
|
32
|
+
pattern origin by one full tile (240px) over `dur`, then
|
|
33
|
+
loops seamlessly. Edit `dur` for speed; delete this
|
|
34
|
+
<animateTransform> to freeze the flow. --> <animateTransform attributeName="patternTransform" type="translate" from="0 0" to="0 240" dur="30s" repeatCount="indefinite" data-astro-cid-5hce7sga></animateTransform> <g fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" data-astro-cid-5hce7sga> <!-- Stroke color cycle — driven by SMIL inside the SVG
|
|
35
|
+
(not CSS `color`) because Chromium doesn't reliably
|
|
36
|
+
repaint strokes that resolve via currentColor when
|
|
37
|
+
the parent's CSS `color` is animated. Edit the
|
|
38
|
+
`values` list to pick your palette; semicolons
|
|
39
|
+
separate stops, last value should match first to
|
|
40
|
+
close the loop seamlessly. --> <animate attributeName="stroke" values="#818cf8;#f472b6;#4ade80;#22d3ee;#818cf8" dur="10s" repeatCount="indefinite" data-astro-cid-5hce7sga></animate> <!-- Stream A (x=40): big staircase cascade near top, single hook near bottom --> <path d="M 40,0 V 30 H 55 V 45 H 70 V 60 H 55 V 75 H 40 V 175 H 25 V 195 H 40 V 240" data-astro-cid-5hce7sga></path> <!-- Stream B (x=100): small step near top, three-tier cascade in middle --> <path d="M 100,0 V 60 H 85 V 80 H 100 V 130 H 115 V 145 H 130 V 160 H 115 V 175 H 100 V 240" data-astro-cid-5hce7sga></path> <!-- Stream C (x=160): three small hooks distributed top-to-bottom --> <path d="M 160,0 V 20 H 175 V 35 H 160 V 110 H 145 V 130 H 160 V 200 H 175 V 220 H 160 V 240" data-astro-cid-5hce7sga></path> <!-- Stream D (x=220): single deep cascade in the upper-middle --> <path d="M 220,0 V 85 H 205 V 100 H 190 V 115 H 220 V 240" data-astro-cid-5hce7sga></path> </g> </pattern> </defs> <rect width="100%" height="100%" fill="url(#celilo-streams)" data-astro-cid-5hce7sga></rect> </svg> <nav data-astro-cid-5hce7sga> <div class="nav-inner" data-astro-cid-5hce7sga> <a href="/" class="nav-logo" data-astro-cid-5hce7sga>celilo</a> <ul class="nav-links" data-astro-cid-5hce7sga> <li data-astro-cid-5hce7sga> <a href="/docs" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M4 5a2 2 0 0 1 2-2h11v18H6a2 2 0 0 1-2-2V5z" data-astro-cid-5hce7sga></path> <path d="M9 7h6M9 11h6M9 15h4" data-astro-cid-5hce7sga></path> </svg>
|
|
41
|
+
docs
|
|
42
|
+
</a> </li> <li data-astro-cid-5hce7sga> <a href="/modules" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <rect x="3" y="3" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> <rect x="14" y="3" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> <rect x="3" y="14" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> <rect x="14" y="14" width="7" height="7" rx="1" data-astro-cid-5hce7sga></rect> </svg>
|
|
43
|
+
modules
|
|
44
|
+
</a> </li> <li data-astro-cid-5hce7sga> <a href="/vision" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z" data-astro-cid-5hce7sga></path> <circle cx="12" cy="12" r="3" data-astro-cid-5hce7sga></circle> </svg>
|
|
45
|
+
vision
|
|
46
|
+
</a> </li> <li data-astro-cid-5hce7sga> <a href="https://github.com/celilo/celilo" target="_blank" rel="noopener" data-astro-cid-5hce7sga> <svg viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M9 19c-4.3 1.4-4.3-2.5-6-3m12 5v-3.5c0-1 .1-1.4-.5-2 2.8-.3 5.5-1.4 5.5-6a4.6 4.6 0 0 0-1.3-3.2 4.2 4.2 0 0 0-.1-3.2s-1.1-.3-3.5 1.3a12.3 12.3 0 0 0-6.2 0C6.5 2.8 5.4 3.1 5.4 3.1a4.2 4.2 0 0 0-.1 3.2A4.6 4.6 0 0 0 4 9.5c0 4.6 2.7 5.7 5.5 6-.6.6-.6 1.2-.5 2V21" data-astro-cid-5hce7sga></path> </svg>
|
|
47
|
+
github
|
|
48
|
+
</a> </li> <li data-astro-cid-5hce7sga> <button type="button" class="theme-toggle" id="theme-toggle" aria-label="Toggle light/dark theme" title="Toggle theme" data-astro-cid-5hce7sga> <svg class="icon-sun" viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <circle cx="12" cy="12" r="4" data-astro-cid-5hce7sga></circle> <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" data-astro-cid-5hce7sga></path> </svg> <svg class="icon-moon" viewBox="0 0 24 24" aria-hidden="true" data-astro-cid-5hce7sga> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" data-astro-cid-5hce7sga></path> </svg> </button> </li> </ul> </div> </nav> <script>
|
|
49
|
+
// Click handler for the toggle. Reads the current data-theme,
|
|
50
|
+
// flips it, persists to localStorage, applies to <html>. The
|
|
51
|
+
// resolution script in <head> already set the initial value
|
|
52
|
+
// (system-aware) before paint, so this only runs on user
|
|
53
|
+
// interaction.
|
|
54
|
+
(function () {
|
|
55
|
+
const btn = document.getElementById('theme-toggle');
|
|
56
|
+
if (!btn) return;
|
|
57
|
+
btn.addEventListener('click', function () {
|
|
58
|
+
const current = document.documentElement.getAttribute('data-theme');
|
|
59
|
+
const next = current === 'light' ? 'dark' : 'light';
|
|
60
|
+
document.documentElement.setAttribute('data-theme', next);
|
|
61
|
+
try {
|
|
62
|
+
localStorage.setItem('celilo-theme', next);
|
|
63
|
+
} catch (_e) {
|
|
64
|
+
// localStorage unavailable (private mode) — toggle still
|
|
65
|
+
// works for the session, just doesn't persist.
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
})();
|
|
69
|
+
</script> <main data-astro-cid-5hce7sga> <section class="hero" data-astro-cid-j7pv25f6> <p class="hero-eyebrow" data-astro-cid-j7pv25f6>home lab orchestration</p> <h1 data-astro-cid-j7pv25f6>Infrastructure, done right.</h1> <p class="hero-subtitle" data-astro-cid-j7pv25f6>
|
|
70
|
+
Celilo takes your home lab from a pile of machines and domain names to a
|
|
71
|
+
fully-orchestrated, reproducible infrastructure — with one CLI and a library
|
|
72
|
+
of battle-tested modules.
|
|
73
|
+
</p> <div class="hero-actions" data-astro-cid-j7pv25f6> <a href="/docs" class="btn btn-primary" data-astro-cid-j7pv25f6>Get started →</a> <a href="/modules" class="btn btn-secondary" data-astro-cid-j7pv25f6>Browse modules</a> </div> </section> <div class="terminal" data-astro-cid-j7pv25f6> <div class="terminal-bar" data-astro-cid-j7pv25f6> <span class="dot dot-red" data-astro-cid-j7pv25f6></span> <span class="dot dot-yellow" data-astro-cid-j7pv25f6></span> <span class="dot dot-green" data-astro-cid-j7pv25f6></span> <span class="terminal-title" data-astro-cid-j7pv25f6>celilo — zsh</span> </div> <div class="terminal-body" data-astro-cid-j7pv25f6> <p class="cmd" data-astro-cid-j7pv25f6>celilo module import caddy</p> <p class="out" data-astro-cid-j7pv25f6>✓ Downloading caddy 2.8.4+1 from celilo.computer...</p> <p class="out" data-astro-cid-j7pv25f6>→ Running on_install hook</p> <p class="out" data-astro-cid-j7pv25f6> → firewall.exposeService (ports 80, 443)</p> <p class="out" data-astro-cid-j7pv25f6> ✓ firewall.exposeService → 203.0.113.42</p> <p class="out" data-astro-cid-j7pv25f6> → dns_registrar.registerHost (www → 203.0.113.42)</p> <p class="out" data-astro-cid-j7pv25f6> ✓ dns_registrar.registerHost</p> <p class="ok" data-astro-cid-j7pv25f6>✓ caddy installed — https://www.example.com</p> </div> </div> <section data-astro-cid-j7pv25f6> <h2 class="section-title" data-astro-cid-j7pv25f6>Everything you need to run a home lab.</h2> <p class="section-sub" data-astro-cid-j7pv25f6>
|
|
74
|
+
Modules for networking, identity, DNS, web hosting, and more — all wired together automatically.
|
|
75
|
+
</p> <div class="features" data-astro-cid-j7pv25f6> <div class="feature" data-astro-cid-j7pv25f6> <div class="feature-icon" data-astro-cid-j7pv25f6>🔌</div> <h3 data-astro-cid-j7pv25f6>Capability system</h3> <p data-astro-cid-j7pv25f6>
|
|
76
|
+
Modules declare what they provide and what they need. Celilo wires them
|
|
77
|
+
together automatically — no YAML templating, no hardcoded IPs.
|
|
78
|
+
</p> </div> <div class="feature" data-astro-cid-j7pv25f6> <div class="feature-icon" data-astro-cid-j7pv25f6>📦</div> <h3 data-astro-cid-j7pv25f6>Module registry</h3> <p data-astro-cid-j7pv25f6>
|
|
79
|
+
Install modules with one command. Each module bundles Terraform,
|
|
80
|
+
Ansible, and TypeScript hooks into a single <code data-astro-cid-j7pv25f6>.netapp</code> file.
|
|
81
|
+
</p> </div> <div class="feature" data-astro-cid-j7pv25f6> <div class="feature-icon" data-astro-cid-j7pv25f6>🔒</div> <h3 data-astro-cid-j7pv25f6>Secrets first</h3> <p data-astro-cid-j7pv25f6>
|
|
82
|
+
Secrets are encrypted at rest and injected at runtime. Modules never
|
|
83
|
+
touch plaintext credentials — they receive typed, validated values.
|
|
84
|
+
</p> </div> <div class="feature" data-astro-cid-j7pv25f6> <div class="feature-icon" data-astro-cid-j7pv25f6>🛡️</div> <h3 data-astro-cid-j7pv25f6>Reproducible deploys</h3> <p data-astro-cid-j7pv25f6>
|
|
85
|
+
Every module generates complete Terraform and Ansible projects.
|
|
86
|
+
Destroy and rebuild your infrastructure from source at any time.
|
|
87
|
+
</p> </div> <div class="feature" data-astro-cid-j7pv25f6> <div class="feature-icon" data-astro-cid-j7pv25f6>🌐</div> <h3 data-astro-cid-j7pv25f6>Automatic HTTPS</h3> <p data-astro-cid-j7pv25f6>
|
|
88
|
+
Caddy handles TLS termination. Add a module, get a domain. DNS is
|
|
89
|
+
updated, certificates are provisioned — all in one step.
|
|
90
|
+
</p> </div> <div class="feature" data-astro-cid-j7pv25f6> <div class="feature-icon" data-astro-cid-j7pv25f6>🐕</div> <h3 data-astro-cid-j7pv25f6>Dogfooded</h3> <p data-astro-cid-j7pv25f6>
|
|
91
|
+
This website is a Celilo module. The registry that hosts these modules
|
|
92
|
+
is a Celilo module. We eat our own cooking.
|
|
93
|
+
</p> </div> </div> </section> </main> <footer data-astro-cid-5hce7sga> <p data-astro-cid-5hce7sga>Celilo — home lab orchestration. Built with care.</p> </footer> </body></html>
|