From ce0bed8d1330c43af7ba7de0ce0b98298d15f628 Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:41:22 +0200 Subject: [PATCH 01/10] docs(security): document the leak gate and its one allowlisted decoy .env --- SECURITY.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SECURITY.md b/SECURITY.md index b15c38084..410168456 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -13,3 +13,12 @@ live malware, private keys, production `.env` files, packet captures containing private traffic, or unredacted logs to a public issue. Supported security fixes target the current `main` branch. + +`scripts/check-public-leaks.py` enforces the "leak a real secret" half of this +policy on every change, and fails CI on private keys, GitHub/AWS/Slack tokens, +literal credential assignments, credentials embedded in URLs, deployment +`.env` files, private-key and packet-capture binaries, and the +deployment-specific addresses and default password this repository must never +name. Exactly one tracked `.env` is exempt, and it is the decoy honeyfs file +under `arcane/home/honeypot-cowrie/` that exists for attackers to find — not a +credential, and not something to "fix" by deleting. From 06638925e9727bb62262ae456b9df2cedbac345b Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:42:15 +0200 Subject: [PATCH 02/10] docs(knowledge-store): record that the vault worker is authored but not deployed --- docs/knowledge-store-design.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/knowledge-store-design.md b/docs/knowledge-store-design.md index 32a88cfdf..0bfa596a4 100644 --- a/docs/knowledge-store-design.md +++ b/docs/knowledge-store-design.md @@ -4,6 +4,25 @@ Decision record for #2289, gating #2290–#2292. No code changes ship with this document — it is the design pass #1634 asked for before any ingest worker exists. +> **Status, re-measured 2026-09-27: authored and tracked, not deployed.** The +> ingest worker does exist in git — `vault-worker/` carries a `worker.py`, a +> `sanitize.py`, a `Dockerfile` and two compose files, the shipped output of +> #2289/#2290 — but nothing deploys it. It is absent from +> `arcane/manifests/home-production.json`; no vault-worker container has ever +> run on the homeserver, and there is no `vault-worker` stack directory among +> the deployed ones; Elasticsearch carries no `knowledge-vault*` index, so the +> `knowledge-vault-state-v1` checkpoint cited for this worker in +> [PIPELINES.md](PIPELINES.md) does not exist either (that row is planned, the +> same way); and the live APIARY worker runs +> `WORKER_LOOPS=alert-notifier,attacker-identity,agent-intrusion,correlator,dashboard-rollups,threat-intel,zeek-proxy-attribution` +> — there is no vault loop in it. +> +> So everything below is the design and implementation record of a **planned** +> subsystem, kept because #2289/#2290 shipped real code against it. Read it as +> intent that has not been switched on: none of the paths, indices or +> checkpoints it names exist on a live host, and no deployment decision is +> recorded anywhere in the repository. + ## 1. Storage: plain markdown directory, carried by the existing off-host backup path, not git/Syncthing From 9987dcc4f47dd1ffa3188a64757f6f7503bbe494 Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:44:15 +0200 Subject: [PATCH 03/10] docs(design-lab): mark the public copy redacted and pre-#1628 snapshot --- docs/design-lab/design-notes.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/design-lab/design-notes.md b/docs/design-lab/design-notes.md index 2dc559161..b18a76c6b 100644 --- a/docs/design-lab/design-notes.md +++ b/docs/design-lab/design-notes.md @@ -1,4 +1,19 @@ # APIARY dashboard design review — dashboard.example — 2026-08-17 + +> **Public, redacted copy — status as of 2026-09-27.** The address redaction in +> this file is deliberate and correct as it stands: the only address literals +> here are `127.0.0.1` (loopback) and `203.0.113.1` (RFC 5737 TEST-NET-3), and +> the only hostname is a reserved `.example` domain. Do not substitute real +> values for them, and do not restore anything redacted out of the source copy. +> +> The findings below are a **snapshot of a review session on 2026-08-17, not a +> description of the current code**. Several cite the Go dashboard's static +> assets and route table by name (`hp-app.js`, `hp-dynamic-nav.js`, +> `routes.go`); that dashboard was deleted in #1628 on 2026-08-22, five days +> after this review, and none of those files exist in the repository any more. +> The current route authority is the Rust `axum` service at +> `arcane/home/honeypot-dashboard/backend-service/src/main.rs`, so re-locate a +> finding's code there before acting on it. ## Findings (running) - Overview (light): loads fast, authenticated. Heatmap "Activity — last 24h" dominates; lower rows (multipot, conpot-kamstrup, endlessh) appear near-empty/pale — visual weight wasted? - Theme toggle: monitor icon top-right (left of LIVE). Dark theme renders correctly on Overview. From 2b445254e1a33afd19fc81b3a0638003c3109000 Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:45:08 +0200 Subject: [PATCH 04/10] docs(knowledge-store): repoint stale line citations at the code as it is The design record cited four source locations by line number, and all four had drifted: the two-flag llm-worker gate is at worker.py:246-248 and :314-318 (not :200-202/:254-264, which land in an IP-locality helper and in the embedding-model config), the backup-honeypot.sh archive walk is at :119 (not :87, which is the compose-file existence check), and the auto_sync = 0 references in ARCANE-GIT-SYNC.md are at :425 and :478 (not :321/:374, a service list and a manual-sync endpoint). No prose change: the claims themselves all still hold. --- docs/knowledge-store-design.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/knowledge-store-design.md b/docs/knowledge-store-design.md index 0bfa596a4..bcb0c2c41 100644 --- a/docs/knowledge-store-design.md +++ b/docs/knowledge-store-design.md @@ -59,8 +59,8 @@ stack, not a reuse of one, even though it is a trivial one to stand up precedent for "git as a sync/deploy substrate" in this repo is Arcane's own GitOps machinery (`docs/ARCANE-GIT-SYNC.md`), which already runs a git-pull-and-apply loop against `main` with `auto_sync = 0` set deliberately -on rows that must not auto-follow (`docs/ARCANE-GIT-SYNC.md:321` and -`docs/ARCANE-GIT-SYNC.md:374`) — i.e. +on rows that must not auto-follow (`docs/ARCANE-GIT-SYNC.md:425` and +`docs/ARCANE-GIT-SYNC.md:478`) — i.e. this codebase's existing git-sync tooling defaults to *manual* triggers for anything sensitive, which is the posture this decision adopts too (see §4). @@ -191,7 +191,7 @@ A persistent, curated, cross-referenced copy of (bounded, redacted) attacker material is qualitatively different from the raw per-event ES documents it's derived from: it's smaller, denser, and easier for a human or a script to sweep in one pass. Reading `analysis/backup-honeypot.sh`, its archive step -(`backup-honeypot.sh:87`) already walks `./analysis ./dashboard ./personas +(`backup-honeypot.sh:119`) already walks `./analysis ./dashboard ./personas ./state` by directory-existence check, unconditionally including anything found there. If the vault directory (§1: `state/knowledge-vault/`) is placed under `$stack_dir/state/`, it is **already** inside this glob and would start @@ -216,8 +216,8 @@ inferring it from a directory glob matching by accident. ### Worker authorization gate The vault-ingest worker (#2290) must gate non-dry-run writes the same way -`llm-worker` gates captured-data mode. Reading `llm-worker/worker.py:200-202` -and `llm-worker/worker.py:254-264`: +`llm-worker` gates captured-data mode. Reading `llm-worker/worker.py:246-248` +and `llm-worker/worker.py:314-318`: non-dry-run requires `LLM_ENABLED=true` **and** `LLM_ALLOW_CAPTURED_DATA=true` together, with the error message naming both. The vault worker adopts the same two-flag shape (its own env var names, e.g. `VAULT_ENABLED` / From b0478b3c6ee6b6aafa30d119d6a19efb54eacbdf Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:45:40 +0200 Subject: [PATCH 05/10] docs(rocky,host-tuning): record that the Rocky migration landed and measure tuning state The homeserver was re-provisioned from Ubuntu to Rocky Linux 10.2 on 2026-09-03, so both of these documents were describing a migration that had already happened. Add a dated status banner to each rather than rewriting prose that is not wrong. ROCKY-10-MIGRATION.md: the rhel branch of install-homeserver.sh is now the production path, not a smoke-test rehearsal. Re-checked every technical claim against scripts/install-homeserver.sh and scripts/lib/install-common.sh -- the pkg_update/pkg_install shims, the once-at-source-time $DISTRO_FAMILY resolution (ID first, then ID_LIKE), the full package-name table, the verbatim Docker centos repofile, the cuda-rhel10 repo, the container_use_devices boolean, and the non-fatal step_preflight_rhel_platform all still match, and the base OS is still installed by hand because no kickstart artifact exists in the tree. The ":z/:Z label gap" note is still accurate: none of the 34 compose files under arcane/home/ carries an SELinux relabel. HOST-TUNING.md: read-only re-measure over `ssh homeserver` shows the script is not fully applied -- zram-generator is not installed and no zram module is loaded (the zram-generator.conf present is hand-written, not the file the script writes), the udev scheduler rule is absent, and noatime is on /var but not on the /home mount the new install added. Only the tuned profile is in effect. The 20 GB card figure is still right (20475 MiB live). --- docs/HOST-TUNING.md | 28 ++++++++++++++++++++++++++++ docs/ROCKY-10-MIGRATION.md | 25 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/docs/HOST-TUNING.md b/docs/HOST-TUNING.md index afe407186..b88cfdd8a 100644 --- a/docs/HOST-TUNING.md +++ b/docs/HOST-TUNING.md @@ -8,6 +8,34 @@ sudo ./scripts/tune-rocky10.sh --dry-run # show what would change sudo ./scripts/tune-rocky10.sh # apply ``` +> **Status (2026-09-27): the script is not fully applied on the live homeserver.** +> The host was re-provisioned onto Rocky Linux 10.2 on 2026-09-03, and the +> install it now has differs from the one this script was written against. Read +> the checklist in "Verifying afterwards" as *the intended end state*, not as a +> description of the box — re-measured read-only over `ssh homeserver` on +> 2026-09-27, it currently fails two of its six lines: +> +> - **#1 (zram) is not in effect.** `zram-generator` is not installed, no zram +> module is loaded, `zramctl` prints nothing, and `swapon --show` lists only +> the 32G LVM swap. An `/etc/systemd/zram-generator.conf` *does* exist, but it +> is not the file this script writes: it has no `# Managed by` header and +> sizes the device with `min(ram / 2, 32768)` rather than the computed MB +> value the script emits, so it was hand-written — and nothing consumes it +> while the package is missing. +> - **#2 (scheduler rule) is not in effect.** +> `/etc/udev/rules.d/60-apiary-ioscheduler.rules` does not exist. The +> schedulers that *are* set came from elsewhere: `nvme0n1` is on `none` and +> the 8.7T rotational `sdb` is on `mq-deadline`, but the non-rotational `sda` +> is also on `none` where this script's rule would put it on `kyber`. +> - **#4 (noatime) is half done.** `/var` carries `noatime` in `/etc/fstab`; +> `/home`, which is a separate mount on this install and did not exist in +> `/etc/fstab` before the re-provision, is still `defaults`. +> - **#5 (CPU governor) is in effect** — `tuned-adm active` reports +> `throughput-performance` and cpu0's governor is `performance`. +> +> The 20 GB figure in the sizing note below is still right: the live +> `NVIDIA RTX 4000 Ada Generation` reports 20475 MiB. + These are the Rocky/RHEL-family equivalents of the tunings, not a transliteration of the Debian recipe. The differences are the point: the Debian instructions do not work here, and two of them do not survive a reboot diff --git a/docs/ROCKY-10-MIGRATION.md b/docs/ROCKY-10-MIGRATION.md index c19f7bba9..2c522e844 100644 --- a/docs/ROCKY-10-MIGRATION.md +++ b/docs/ROCKY-10-MIGRATION.md @@ -1,5 +1,30 @@ # Rocky Linux 10 support in `install-homeserver.sh` +> **Status (2026-09-27): the move is done — the homeserver now runs Rocky Linux +> 10.2.** The RHEL path below is no longer a smoke-test target or a rehearsal +> for a future migration: the host was re-provisioned onto Rocky 10.2 +> (2026-09-03, Anaconda) with a different disk layout, so the `rhel` branches in +> `install-homeserver.sh` are the production path and `debian` is the fallback +> that only a rebuild would exercise. Re-measured read-only over +> `ssh homeserver` on 2026-09-27: `/etc/os-release` reports `ID=rocky` and +> `VERSION="10.2 (Red Quartz)"`, and both conditions in +> "Two things Rocky does that Ubuntu did not" are live — SELinux is `Enforcing` +> and firewalld is `active`. +> +> The two things that stay open are unchanged and are the ones to read first: +> the base OS is still installed by hand (there is still no kickstart artifact +> in the tree), and the `:z`/`:Z` label gap below is still real — none of the +> 34 compose files under `arcane/home/` carries an SELinux relabel. +> +> Everything else in this document was re-checked against +> `scripts/install-homeserver.sh` and `scripts/lib/install-common.sh` on +> 2026-09-27 and still matches: the `pkg_update`/`pkg_install` shims, the +> once-at-source-time `$DISTRO_FAMILY` resolution, the full package-name +> table, the verbatim Docker `centos` repofile, the `cuda-rhel10` repo, the +> `container_use_devices` boolean, and the non-fatal +> `step_preflight_rhel_platform`. For the current disk layout see +> [`HOMESERVER-DISK-LAYOUT.md`](HOMESERVER-DISK-LAYOUT.md). + The homeserver is moving from Ubuntu to Rocky Linux 10. `scripts/install-homeserver.sh` now runs on both, so the reinstall smoke test in #1609 has a working installer. From 112319b2326a49dea57c46ccab069df15c455af6 Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:47:55 +0200 Subject: [PATCH 06/10] docs(keycloak): record that the hard cutover has shipped The file still opened with 'Status: accepted Phase 0 decision record', which reads as a proposal, but the cutover it specifies has been carried out. Adds a dated status note recording what the repository shows today and leaves the contract text untouched. Verified against the repo, not asserted: honeypot-keycloak is a manifest entry and a deployed stack; the matrix's six 'Isolated gateway' rows map one-to-one onto the six oauth2-proxy services in vps/docker-compose.yml (oidc-kibana, oidc-tanner, oidc-evebox, oidc-arkime, oidc-revdeck, oidc-traefik) sharing one image definition; and every identifier on the hard-cutover removal list is now absent from vps/ and arcane/, surviving only in documentation. The Xore/auth-backend sentence needed no change -- it claims the theme only, never a running service, which is what SENSORS.md already says. --- docs/KEYCLOAK-CUTOVER.md | 52 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/docs/KEYCLOAK-CUTOVER.md b/docs/KEYCLOAK-CUTOVER.md index a9a244d3f..3670ddfa8 100644 --- a/docs/KEYCLOAK-CUTOVER.md +++ b/docs/KEYCLOAK-CUTOVER.md @@ -2,6 +2,39 @@ Status: accepted Phase 0 decision record for #976 and epic #986. +> **Status, verified against the repository 2026-09-27: the cutover is +> implemented, not pending.** The line above is the decision that was accepted +> at Phase 0; the hard cutover it specifies has since been carried out, so read +> this file as the contract that governs the live identity tier rather than as a +> proposal. What the repository shows today: +> +> - `honeypot-keycloak` is a manifest entry +> (`arcane/manifests/home-production.json`) and a deployed stack +> (`arcane/home/honeypot-keycloak/compose.yml` — Keycloak plus its +> PostgreSQL), which is the "Fixed architecture" section's `honeypot-keycloak` +> stack. +> - The six "Isolated gateway" rows in the matrix below correspond one-to-one to +> six `oauth2-proxy` services in `vps/docker-compose.yml`: `oidc-kibana`, +> `oidc-tanner`, `oidc-evebox`, `oidc-arkime`, `oidc-revdeck` and +> `oidc-traefik` (one `quay.io/oauth2-proxy/oauth2-proxy` image definition, +> six services sharing its `oidc-gateway-environment` anchor). `arcane.*` and +> the `dashboard.*`/`honeypot.*` row correctly have no gateway. +> - Every identifier on the **Hard-cutover removal list** below is gone from +> live configuration. `auth-portal`, `strip-auth-identity`, `xore_sso`, +> `AUTH_INTROSPECTION_URL` and `AUTH_TARGET_HOST` now appear only in +> documentation (this file, `docs/TESTING.md` and the dated +> `docs/research/518-smoke-test-research.md` record), never in `vps/` or +> `arcane/`. No `forward-auth`/`forwardAuth` middleware reference remains +> under `vps/` or `arcane/`. +> - The `Xore/auth-backend` line below is accurate as written and should not be +> read as a live dependency: the *runtime* is retired and only the +> presentation-only theme is still sourced from that repository, which is +> what `docs/SENSORS.md` and `docs/KEYCLOAK-OPERATIONS.md` already say. +> +> The contract text itself is unchanged and still accurate; this note records +> only that it has shipped. Day-to-day administration lives in +> [`KEYCLOAK-OPERATIONS.md`](KEYCLOAK-OPERATIONS.md). + Implementation and operations are documented in [`KEYCLOAK-OPERATIONS.md`](KEYCLOAK-OPERATIONS.md). `Xore/auth-backend` owns exactly the presentation-only `themes/apiary` Keycloak theme, checked out @@ -37,17 +70,26 @@ pinned Keycloak runtime does not provide a recovery-code required-action factory | Route | Consumer | Integration | Keycloak client | Required role | Trust boundary and special traffic | |---|---|---|---|---|---| | `dashboard.*`, `honeypot.*` | APIARY dashboard, APIs, SSE, exports, embedded settings | Native authorization-code OIDC with PKCE | `apiary-dashboard` | `user`; mutations require `admin` | Dashboard validates tokens and owns its session. No proxy identity headers. PDF routes use the same dashboard session. | -| `kibana.*` | Kibana | Isolated gateway | `kibana` | `user` | Gateway is the only network peer allowed to reach Kibana; preserve WebSockets and base paths. | -| `evebox.*` | EveBox | Isolated gateway | `evebox` | `user` | Gateway is the only upstream path; preserve API, stream, and download behavior. | -| `arkime.*` | Arkime | Isolated gateway | `arkime` | `user` | Arkime trusts the gateway-injected `X-Forwarded-User` identity (`authMode=header+digest`, #979) -- safe only because the upstream is unreachable except through the gateway (verified) and oauth2-proxy strips any client-supplied copy of that header before injecting its own (`OAUTH2_PROXY_SKIP_AUTH_STRIP_HEADERS`, pinned). The pre-existing local "admin" digest account remains as a fallback. | -| `tanner.*` | TANNER UI | Isolated gateway | `tanner` | `user` | Gateway-only upstream network; preserve API and static assets. | -| `rev.*` | RevDeck/Ghidra UI | Isolated gateway | `revdeck` | `user` | Gateway-only upstream network; preserve long responses, downloads, and streams. | +| `kibana.*` | Kibana | Isolated gateway | `kibana` | `access` | Gateway is the only network peer allowed to reach Kibana; preserve WebSockets and base paths. | +| `evebox.*` | EveBox | Isolated gateway | `evebox` | `access` | Gateway is the only upstream path; preserve API, stream, and download behavior. | +| `arkime.*` | Arkime | Isolated gateway | `arkime` | `access` | Arkime trusts the gateway-injected `X-Forwarded-User` identity (`authMode=header+digest`, #979) -- safe only because the upstream is unreachable except through the gateway (verified) and oauth2-proxy strips any client-supplied copy of that header before injecting its own (`OAUTH2_PROXY_SKIP_AUTH_STRIP_HEADERS`, pinned). The pre-existing local "admin" digest account remains as a fallback. | +| `tanner.*` | TANNER UI | Isolated gateway | `tanner` | `access` | Gateway-only upstream network; preserve API and static assets. | +| `rev.*` | RevDeck/Ghidra UI | Isolated gateway | `revdeck` | `access` | Gateway-only upstream network; preserve long responses, downloads, and streams. | | `traefik.*` | Traefik read-only dashboard | Isolated gateway | `traefik-dashboard` | `admin` | Gateway fronts `api@internal`; callback is excluded from recursive auth. | | `arcane.*` | Arcane administrator UI (#1185, Dockge's replacement -- Dockge decommissioned) | Native authorization-code OIDC | `arcane` | `admin` | No gateway: Arcane authenticates directly against Keycloak. Root-equivalent risk (`/var/run/docker.sock` mounted read-write) -- `admin` role is granted only to the `administrators` group. | | `auth.example.invalid` | OIDC login, discovery, JWKS, account console | Direct Keycloak | n/a | public protocol endpoints; authenticated account actions | Rate-limited edge route to the Keycloak WireGuard bridge. | | `auth.example.invalid/admin` | Keycloak administration | Direct Keycloak | n/a | Keycloak administrator + MFA | Same host as the issuer (#1028); a `PathPrefix(/admin)` router gives the SPA's bootstrap burst a larger rate limit. Keycloak owns authentication; HTTP Basic would conflict with the SPA's Bearer API calls. | | decoy/static/API/status/file/blog hosts currently lacking `forward-auth` | Public honeypot or explicitly application-owned auth | Public / unchanged | none | none | Never attach operator SSO merely because the hostname exists. Public collection must remain independent of IdP availability. | +Role names differ per row on purpose. The realm's low-privilege client role is +`access` -- granted to the `users` group -- and every gateway enforces exactly +`:access` through `OAUTH2_PROXY_ALLOWED_ROLES` in +`vps/docker-compose.yml`; `traefik-dashboard` and `arcane` deliberately use +`admin` instead (#1014/#1185, root-equivalent). The dashboard row's `user` is +*not* a Keycloak role name: `resource_access.apiary-dashboard.roles` carries +`access` and `admin`, and the dashboard collapses them to its own +`user`/`admin` session role. + The deployment validator must fail when a protected router has neither native OIDC ownership nor its named gateway. A redirect alone is not evidence: each row must retain an authorized real-page/API check and an unauthorized denial From 5c15c540a323f8b7503f4e1a9054870b7cee4b5c Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:48:29 +0200 Subject: [PATCH 07/10] docs(knowledge-store): point the backup-scope decision at the comment that already records it Two claims in the design record had fallen behind the tree, both in the direction of "this still has to happen" when it already has: - Section 4 said the decision to let the vault inherit backup-honeypot.sh's existing scope "is a decision to record verbatim in that script's own comment block when #2290 lands the directory". It is already recorded, at backup-honeypot.sh:107-114, naming #2289, #2290 and this document. - Section 1 described Arcane's GitOps rows as carrying `auto_sync = 0` "set deliberately on rows that must not auto-follow". ARCANE-GIT-SYNC.md:475-480 records the opposite: auto_sync is 0 on *every* live row, including the three the manifest flags autoSync: true, which it calls silently inert. The conclusion the record draws from it -- that this codebase's git-sync tooling defaults to manual triggers -- is still what the source says ("Every deploy is manual"), so the reasoning stands; only the characterisation of the mechanism was wrong. Everything else verified, no change needed: the vault-worker tree (worker.py, sanitize.py, Dockerfile, two compose files), its absence from arcane/manifests/home-production.json, the knowledge-vault-state-v1 row in PIPELINES.md, and every code anchor in the record (config_history.rs:14-15,50,57-88; problem_reports.rs:32,38-74; credentials.rs:8-11; llm-worker/worker.py:246-248,314-318; backup-honeypot.sh:15-17,119; ARCANE-GIT-SYNC.md:425,478). --- docs/knowledge-store-design.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/knowledge-store-design.md b/docs/knowledge-store-design.md index bcb0c2c41..fa7fcb1cc 100644 --- a/docs/knowledge-store-design.md +++ b/docs/knowledge-store-design.md @@ -58,8 +58,9 @@ stack, not a reuse of one, even though it is a trivial one to stand up (`git init` in a directory, a commit per note-write batch). The nearest real precedent for "git as a sync/deploy substrate" in this repo is Arcane's own GitOps machinery (`docs/ARCANE-GIT-SYNC.md`), which already runs a -git-pull-and-apply loop against `main` with `auto_sync = 0` set deliberately -on rows that must not auto-follow (`docs/ARCANE-GIT-SYNC.md:425` and +git-pull-and-apply loop against `main` and where every live row currently +carries `auto_sync = 0`, with every deploy still a manual +sync → build → redeploy (`docs/ARCANE-GIT-SYNC.md:425` and `docs/ARCANE-GIT-SYNC.md:478`) — i.e. this codebase's existing git-sync tooling defaults to *manual* triggers for anything sensitive, which is the posture this decision adopts too (see §4). @@ -208,10 +209,11 @@ above already bounds and strips what can land in a note, the vault's content is closer in sensitivity to the config material `backup-honeypot.sh` already carries than to the bulk payload/PCAP data it explicitly excludes — so extending that script's existing scope to include it is the correct call, -not an oversight to patch around later. This is a decision to record -verbatim in `analysis/backup-honeypot.sh`'s own comment block when #2290 -lands the directory, so a future reader sees it was deliberate rather than -inferring it from a directory glob matching by accident. +not an oversight to patch around later. That decision is already recorded +verbatim in `analysis/backup-honeypot.sh`'s own comment block +(`backup-honeypot.sh:107-114`, which names #2289, #2290 and this document), so +a future reader sees it was deliberate rather than inferring it from a +directory glob matching by accident. ### Worker authorization gate From 944e9a46646070c344ebcf38c80d276f00dfdfc3 Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:51:18 +0200 Subject: [PATCH 08/10] docs(rocky): correct the compose-file count behind the :z/:Z claim The status banner said 'none of the 34 compose files under arcane/home/'. Counted by command, 35 compose files are tracked there: the 34 stack-level compose.yml files, plus a decoy honeyfs compose nested at arcane/home/honeypot-cowrie/cowrie/honeyfs/ that the 34 figure had silently excluded. The substantive claim is unchanged and re-verified: no :z or :Z relabel appears in any of the 35. --- docs/ROCKY-10-MIGRATION.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/ROCKY-10-MIGRATION.md b/docs/ROCKY-10-MIGRATION.md index 2c522e844..f6ed4e979 100644 --- a/docs/ROCKY-10-MIGRATION.md +++ b/docs/ROCKY-10-MIGRATION.md @@ -14,7 +14,10 @@ > The two things that stay open are unchanged and are the ones to read first: > the base OS is still installed by hand (there is still no kickstart artifact > in the tree), and the `:z`/`:Z` label gap below is still real — none of the -> 34 compose files under `arcane/home/` carries an SELinux relabel. +> 35 compose files tracked under `arcane/home/` carries an SELinux relabel (34 +> stack-level `compose.yml` files, plus the decoy honeyfs compose nested inside +> `arcane/home/honeypot-cowrie/cowrie/`, which is an attacker-facing artifact +> rather than a deployed stack). > > Everything else in this document was re-checked against > `scripts/install-homeserver.sh` and `scripts/lib/install-common.sh` on From 682a3bf9631451199b8090534218a83c48093cd4 Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:52:09 +0200 Subject: [PATCH 09/10] docs(host-tuning): name the two numbering schemes the status note mixes The note said the verification checklist 'fails two of its six lines' and then listed three non-compliant tunings (#1, #2, #4), which reads as a contradiction. Both numbers are right: the six lines are the six commands in the code block, and only the two zram lines fail; the bullets are keyed to the table's five tuning numbers, where #2 and #4 are inapplicable as durable changes while the single command observing each still reads correct today. Say so instead of leaving the reader to reconcile it. The measurements are unchanged and were re-confirmed read-only over ssh homeserver on 2026-09-27. --- docs/HOST-TUNING.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/HOST-TUNING.md b/docs/HOST-TUNING.md index b88cfdd8a..74ae01858 100644 --- a/docs/HOST-TUNING.md +++ b/docs/HOST-TUNING.md @@ -13,7 +13,12 @@ sudo ./scripts/tune-rocky10.sh # apply > install it now has differs from the one this script was written against. Read > the checklist in "Verifying afterwards" as *the intended end state*, not as a > description of the box — re-measured read-only over `ssh homeserver` on -> 2026-09-27, it currently fails two of its six lines: +> 2026-09-27, it currently fails two of its six lines (the two `zramctl` / +> `swapon --show` lines; the other four commands pass). The bullets below are +> keyed to the **tuning** numbers in the table, not to those six command lines, +> so they list three tunings rather than two: a tuning can be inapplicable as a +> durable change while the one command that observes it still reads correct +> today. That is exactly the case for #2 and #4. > > - **#1 (zram) is not in effect.** `zram-generator` is not installed, no zram > module is loaded, `zramctl` prints nothing, and `swapon --show` lists only From 8bc1de6486d3de4f5e57ee07b371e592f3f16d2c Mon Sep 17 00:00:00 2001 From: Xore Date: Sun, 27 Sep 2026 16:53:38 +0200 Subject: [PATCH 10/10] docs(keycloak): drop an unevidenced group-to-role grant from the role note The role paragraph said the realm's low-privilege client role is "access -- granted to the users group". The role name is right and is defined in arcane/home/honeypot-keycloak/keycloak/realm/apiary-realm.json, but nothing in the tree records the grant: the realm's `users` and `administrators` groups both carry empty `roleMappings`, and no provisioning script or compose environment assigns either group a client role. So which human holds `access` is a live-realm fact the repository does not contain. State the role names and the gateway enforcement that *is* in the tree (OAUTH2_PROXY_ALLOWED_ROLES is :access for kibana/tanner/evebox/arkime/ revdeck, and admin for traefik-dashboard and arcane), and mark the group membership the matrix implies as an operator-side grant to verify. Everything else in the file re-verified: honeypot-keycloak is a manifest entry with a postgres and keycloak service; the six "Isolated gateway" rows map one-to-one onto the six oidc-* oauth2-proxy services sharing one image and the oidc-gateway-environment anchor; and every identifier on the hard-cutover removal list is absent from vps/ and arcane/, surviving only in documentation. --- docs/KEYCLOAK-CUTOVER.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/KEYCLOAK-CUTOVER.md b/docs/KEYCLOAK-CUTOVER.md index 3670ddfa8..c294a8a70 100644 --- a/docs/KEYCLOAK-CUTOVER.md +++ b/docs/KEYCLOAK-CUTOVER.md @@ -82,13 +82,17 @@ pinned Keycloak runtime does not provide a recovery-code required-action factory | decoy/static/API/status/file/blog hosts currently lacking `forward-auth` | Public honeypot or explicitly application-owned auth | Public / unchanged | none | none | Never attach operator SSO merely because the hostname exists. Public collection must remain independent of IdP availability. | Role names differ per row on purpose. The realm's low-privilege client role is -`access` -- granted to the `users` group -- and every gateway enforces exactly -`:access` through `OAUTH2_PROXY_ALLOWED_ROLES` in -`vps/docker-compose.yml`; `traefik-dashboard` and `arcane` deliberately use -`admin` instead (#1014/#1185, root-equivalent). The dashboard row's `user` is +`access`, and every gateway enforces exactly `:access` through +`OAUTH2_PROXY_ALLOWED_ROLES` in `vps/docker-compose.yml`; +`traefik-dashboard` and `arcane` deliberately use `admin` instead +(#1014/#1185, root-equivalent). The dashboard row's `user` is *not* a Keycloak role name: `resource_access.apiary-dashboard.roles` carries `access` and `admin`, and the dashboard collapses them to its own -`user`/`admin` session role. +`user`/`admin` session role. Which human carries which role is a realm +provisioning decision, not a repository fact: the committed +`keycloak/realm/apiary-realm.json` defines the `users` and `administrators` +groups with empty `roleMappings`, so treat the group membership the matrix +implies as an operator-side grant to be verified in the live realm. The deployment validator must fail when a protected router has neither native OIDC ownership nor its named gateway. A redirect alone is not evidence: each