diff --git a/kb/services/kb-site.md b/kb/services/kb-site.md index 5de6e79..ea3caa7 100644 --- a/kb/services/kb-site.md +++ b/kb/services/kb-site.md @@ -3,14 +3,15 @@ okf: "0.1" type: service visibility: public status: active -updated: 2026-08-04 +updated: 2026-08-05 links: - ../runbooks/kb-site-deploy.md --- # kb-site -Public slice of this knowledge base, served as static HTML at `kb.okit.pl`. +Public slice of this knowledge base, served as static HTML from a deliberately +non-obvious subdomain of `okit.pl`. Plain `nginx:alpine` on the PIHA node reading one Docker named volume — no build step at runtime, no database, no dependencies. @@ -58,6 +59,18 @@ out empty, so every exception is a recorded decision. The check is a release gate: content is copied to the host only after it passes. +## Access + +Three layers keep the site out of casual sight: a query-parameter token enforced +in the nginx/NPM layer (advanced config held in NPM only — the secret is not in +this repository), an unguessable subdomain, and `noindex, nofollow` on every page +alongside a blanket `robots.txt`. + +This is obscurity, not access control. A URL token is written to access logs, +browser history and outbound `Referer` headers, so anyone who obtains a link +keeps it; nothing here resists a deliberate attacker. The leak gate above, not +this, is what keeps private material off the site. + ## Operations Deployment, content refresh, reverse-proxy and DNS setup are described in the diff --git a/scripts/kb/gen_pages.py b/scripts/kb/gen_pages.py index 58d4596..b18658f 100644 --- a/scripts/kb/gen_pages.py +++ b/scripts/kb/gen_pages.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Generator publicznej warstwy bazy wiedzy (kb.okit.pl) z dokumentów OKF v0.1. +"""Generator publicznej warstwy bazy wiedzy z dokumentów OKF v0.1. Wzorzec: ~/narty-2027/saalbach-kb/gen_pages.py — renderer markdown na samej bibliotece standardowej plus prosty, czytelny szablon HTML. Tutaj dochodzi @@ -60,7 +60,7 @@ from check_okf import parse_yaml, split_frontmatter # noqa: E402 REPO_ROOT = Path(__file__).resolve().parents[2] KB_DIR = REPO_ROOT / "kb" DEFAULT_OUT = REPO_ROOT / "build" / "kb-site" -DEFAULT_BASE_URL = "https://kb.okit.pl" +DEFAULT_BASE_URL = "https://kb-e2a24af3.okit.pl" DEFAULT_WHITELIST = Path(__file__).resolve().parent / "check_whitelist.txt" SITE_NAME = "homelab-codex — knowledge base" @@ -464,6 +464,7 @@ def page_shell( + {html.escape(title)} — {html.escape(SITE_NAME)} @@ -816,7 +817,7 @@ def check(out_dir: Path, whitelist_path: Path) -> int: def main() -> int: parser = argparse.ArgumentParser( - description="Generator publicznej warstwy KB (kb.okit.pl) z dokumentów OKF." + description="Generator publicznej warstwy KB z dokumentów OKF." ) parser.add_argument( "--base-url", diff --git a/services/kb-site/docker-compose.yml b/services/kb-site/docker-compose.yml index a7cf8d3..d30af27 100644 --- a/services/kb-site/docker-compose.yml +++ b/services/kb-site/docker-compose.yml @@ -15,6 +15,11 @@ services: # through the helper-container procedure in kb/runbooks/kb-site-deploy.md # (docker cp cannot write into a :ro mount). - kb-site_content:/usr/share/nginx/html:ro + # robots.txt nie pochodzi z generatora — nie jest dokumentem KB, więc + # jedzie z repo i nie ginie przy podmianie zawartości wolumenu. Mount + # pliku jest głębszy niż mount katalogu, więc Docker montuje go później + # i plik przykrywa (nieistniejące) robots.txt z wolumenu. + - ./static/robots.txt:/usr/share/nginx/html/robots.txt:ro # Docker on PIHA has exhausted its default address pools (~30 live stacks: # "all predefined address pools have been fully subnetted"), so this stack # must not ask for a subnet of its own. Declaring a network here suppresses diff --git a/services/kb-site/static/robots.txt b/services/kb-site/static/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/services/kb-site/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /