commit 6758bda3cb5a22fff6c8ce1596b357293a3e2691
Author: Olaf
Date: Thu Oct 8 15:09:55 2026 +0200
NOC site for AS203496 served at noc.obr.sh
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..364fdec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+public/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..314f31f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+# noc-site
+
+Static NOC site for AS203496, served at https://noc.obr.sh. `python3 build.py` writes `public/`.
+
+- Brand name, domain, ASN, upstreams, contacts and PGP keys: `site.json` (rebrand = change `brand` and the contact fields there).
+- Prose: `content/*.txt`.
+- Deploy: copy `public/` to CT114 web-static `/opt/static-sites/sites/noc.obr.sh/current` (see `deploy.sh`).
+- noc.obnh.network returns 301 to https://noc.obr.sh (path kept), set up in the CT110 edge traefik and the CT114 nginx.
diff --git a/build.py b/build.py
new file mode 100755
index 0000000..68bbcd1
--- /dev/null
+++ b/build.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+"""Build the NOC site into ./public. Brand, domain and facts come from site.json; prose from content/*.txt."""
+import json, html, shutil, pathlib
+root = pathlib.Path(__file__).parent
+S = json.load(open(root / "site.json"))
+out = root / "public"
+shutil.rmtree(out, ignore_errors=True)
+shutil.copytree(root / "static", out)
+e = html.escape
+CSS = """:root{--bg:#fff;--fg:#1c1f24;--mut:#5b6470;--ac:#0b5cad;--bd:#d9dee4}
+@media (prefers-color-scheme:dark){:root{--bg:#14171b;--fg:#e6e9ed;--mut:#9aa4b0;--ac:#6cb2ff;--bd:#2d333b}}
+body{margin:0;background:var(--bg);color:var(--fg);font:16px/1.6 system-ui,sans-serif}
+.w{max-width:46rem;margin:0 auto;padding:0 16px}header{border-bottom:1px solid var(--bd);padding:1rem 0}
+header a.b{font-weight:700;color:var(--fg);text-decoration:none}nav{margin-top:.4rem}nav a{margin-right:1rem}
+a{color:var(--ac)}h1{margin-top:2rem}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid var(--bd);padding:.4rem .5rem;text-align:left;vertical-align:top}
+th{color:var(--mut);font-weight:600;width:35%}code{word-break:break-all}footer{color:var(--mut);border-top:1px solid var(--bd);margin:3rem 0 2rem;padding-top:1rem;font-size:.9rem}"""
+NAV = [("/", "Home"), ("/peering/", "Peering"), ("/pgp/", "PGP keys"), ("/contact/", "Contact")]
+def paras(name):
+ return "".join(f"
{e(l)}
" for l in (root / "content" / f"{name}.txt").read_text().splitlines() if l.strip())
+def page(path, title, body):
+ nav = "".join(f'{t}' for u, t in NAV)
+ doc = f"""
+{e(title)} - {e(S['brand'])}
+
+
"""
+page("/contact/", "Contact", cbody)
+(out / "robots.txt").write_text(f"User-agent: *\nAllow: /\nSitemap: https://{S['domain']}/sitemap.xml\n")
+urls = "".join(f"https://{S['domain']}{u}" for u, _ in NAV)
+(out / "sitemap.xml").write_text(f'{urls}')
+print("built", S["domain"])
diff --git a/content/contact.txt b/content/contact.txt
new file mode 100644
index 0000000..7347245
--- /dev/null
+++ b/content/contact.txt
@@ -0,0 +1 @@
+Send operational questions to the NOC address. Report abuse to the abuse address. For emergencies, call the number below. Threema is an alternative channel.
diff --git a/content/home.txt b/content/home.txt
new file mode 100644
index 0000000..ffee1a4
--- /dev/null
+++ b/content/home.txt
@@ -0,0 +1,2 @@
+AS203496 is a small, IPv6-only network for research and training. It exists to learn, test and operate real internet infrastructure the way a production network would.
+The network announces one prefix, 2001:67c:1948::/48, from two sites. Each site has its own transit provider, so the network stays reachable if one upstream fails.
diff --git a/content/peering.txt b/content/peering.txt
new file mode 100644
index 0000000..707b2fc
--- /dev/null
+++ b/content/peering.txt
@@ -0,0 +1,4 @@
+Peering is welcome. AS203496 peers directly where your network shares a facility (a meet-me room) or an internet exchange.
+The network prefers IPv6. Direct sessions are IPv6 only. IPv4 sessions are set up only with IXP route servers, and there are no direct IPv4-only sessions. For direct peerings and interconnects, an MD5 password is appreciated.
+Customers of AS203496 are not treated as peers at any location. Transit can be provided where that is feasible.
+For filtering, use the AS-SET AS203496:AS-OBNH-NETWORK (RIPE). The route6 object and the ROA both cover 2001:67c:1948::/48 with origin AS203496, and an ASPA record lists AS41051 and AS6517 as providers.
diff --git a/content/pgp.txt b/content/pgp.txt
new file mode 100644
index 0000000..168d789
--- /dev/null
+++ b/content/pgp.txt
@@ -0,0 +1 @@
+Public keys for signed and encrypted mail are listed below.
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 0000000..91c701e
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Build and push public/ to web-static CT114 on nibbler (nibbler tailnet 100.82.124.16).
+set -e
+cd "$(dirname "$0")"
+python3 build.py
+tar czf - -C public . | ssh root@100.82.124.16 'pct exec 114 -- sh -c "rm -rf /opt/static-sites/sites/noc.obr.sh/current.new && mkdir -p /opt/static-sites/sites/noc.obr.sh/current.new && tar xzf - -C /opt/static-sites/sites/noc.obr.sh/current.new && rm -rf /opt/static-sites/sites/noc.obr.sh/current.old; [ -d /opt/static-sites/sites/noc.obr.sh/current ] && mv /opt/static-sites/sites/noc.obr.sh/current /opt/static-sites/sites/noc.obr.sh/current.old; mv /opt/static-sites/sites/noc.obr.sh/current.new /opt/static-sites/sites/noc.obr.sh/current"'
diff --git a/site.json b/site.json
new file mode 100644
index 0000000..0af663f
--- /dev/null
+++ b/site.json
@@ -0,0 +1,27 @@
+{
+ "brand": "OBNH Network Baumert",
+ "domain": "noc.obr.sh",
+ "old_domain": "noc.obnh.network",
+ "tagline": "IPv6-only training and research network",
+ "asn": "AS203496",
+ "prefix": "2001:67c:1948::/48",
+ "as_set": "AS203496:AS-OBNH-NETWORK",
+ "upstreams": [
+ {"asn": "AS41051", "name": "FreeTransit (Openfactory)", "site": "leela"},
+ {"asn": "AS6517", "name": "Lake Networks", "site": "kiff"}
+ ],
+ "peeringdb": "https://www.peeringdb.com/net/30644",
+ "contacts": {
+ "noc": "noc@obnh.network",
+ "abuse": "abuse@obnh.network",
+ "phone": "+41 76 263 0152",
+ "threema": "XKSBXYFZ"
+ },
+ "pgp": [
+ ["admin@obnh.network", "05dc62d7ab054e72c6aea639ba0ee08972efd755"],
+ ["admin@obnh.org", "b6475ed1ff426b3c16b7366c3450c39c710127f1"],
+ ["noc@obnh.network", "f50d798f7b7e1a84fe5d0b8f828c4b32f92444c6"],
+ ["olaf@baumert.cc", "cf8e073a7bba64da51308540638a2a01c171b4f9"],
+ ["olaf@obnh.network", "8ac0a93854d0a59ec28c8b4dd0a70dba97115a8a"]
+ ]
+}
diff --git a/static/threema-XKSBXYFZ.jpg b/static/threema-XKSBXYFZ.jpg
new file mode 100644
index 0000000..27c3b09
Binary files /dev/null and b/static/threema-XKSBXYFZ.jpg differ