release(guide): develop -> main — markdown Guide + audience cap, for production deploy #27

Merged
hiveiq merged 84 commits from develop into main 2026-07-16 15:24:26 -04:00
Owner

Release develop -> main so the Guide can be deployed to production.

Why now

main is not an older Guide — it is a different application. It predates the markdown rewrite and, if built, would produce an empty and uncapped Guide:

main (59fce91, 2026-06-29) develop
API /api/v1/guides/{pageKey} /api/v1/guide/nav, /api/v1/guide/modules/{module}
Content 0 markdown files (DB-backed) 426 markdown files
Audience cap none guide.served-audiences

Until this lands, anyone building hiveops-guide from main — including cd-main if it ever starts running — gets an empty Guide with no audience gating at all. That is the main risk this PR retires.

Security: the cap now fails safe (451c475)

The environment cap decided what an instance serves, but defaulted to all four tiers. That made the safe configuration the one you had to remember — a missing or misspelled GUIDE_SERVED_AUDIENCES in production would have served the Architect and Claude tiers to any MSP_ADMIN, and MSP_ADMIN is a customer-facing role (QDS log in as MSP_ADMIN). One forgotten env line, same class as the feedback import that exposed internal Security tickets to QDS.

Default is now customer. Environments opt UP explicitly:

Environment GUIDE_SERVED_AUDIENCES Serves
Production (unset — default) customer: modules + Customer tab (68 of 426 docs)
bcos.dev customer,internal,dev,bcos all four tiers

bcos.dev was pinned to all four tiers first (hiveiq-openmetal-dev b2d4e00, already live on .251) so this cannot narrow it. platform.guide-access is updated too — it documented the old default and told readers the opposite of what the code now does.

Verified in code: the cap is checked in visible() before any role logic, on both read paths (nav() and module()). Role can narrow the set, never widen it past the cap. Caveat recorded in the docs: the cap governs what is served, not what is present — all content still ships in the image.

What is in it

84 commits, 2026-06-30 -> 2026-07-16:

  • Markdown content model — 426 docs across 125 modules; no database (the hiveiq_guide DB is a fossil of the old design and is unused).
  • Per-environment audience capguide.served-audiences (customer < internal < dev < bcos), enforced server-side in MarkdownGuideService.
  • Audience gating fixes, develop-only until now:
    • f21c783 — gate Architect + Claude tabs to BCOS_ADMIN only (#16)
    • 4f84fc5 — dev tier BCOS_ADMIN-only; keep Testing for MSP (#18)
  • Security7174229 bump hiveops-bom 1.0.5 -> 1.0.7, clears httpcore5 + postgresql CVEs.
  • CIe1fcd4c Slack notify made non-fatal.
  • Content: MILESTONE nav section, devops.prod/devops.dev (BCOS-only), fleet.task-permissions (#23), plus ~30 docs commits.

Notes for the reviewer

  • No CI runs against this PR. hiveops-guide has only cd-develop.yml; there is no cd-main, so this merges on approval alone with no automated check (devops#46).
  • Version goes backwards on main: 4.0.0 -> 1.0.0. The rewrite reset the lineage. Harmless while build.sh tags :latest, but worth a conscious nod.
  • No -SNAPSHOT in the pom.

Known follow-up — not fixed here

GuidePanel.svelte in hiveops-incident still calls the OLD contract (/guide/api/v1/guides/{pageKey}, keys incidents.list / incidents.create). That is the root cause of feedback #788 ("Guide button not working for MSP") — an unmigrated consumer, not a typo. It needs repointing to /guide/api/v1/guide/modules/{module} with incident.* keys. The Guide button stays dead until that lands, deploy or no deploy.

🤖 Generated with Claude Code

Release `develop` -> `main` so the Guide can be deployed to production. ## Why now `main` is not an older Guide — it is a **different application**. It predates the markdown rewrite and, if built, would produce an empty and **uncapped** Guide: | | `main` (`59fce91`, 2026-06-29) | `develop` | |---|---|---| | API | `/api/v1/guides/{pageKey}` | `/api/v1/guide/nav`, `/api/v1/guide/modules/{module}` | | Content | 0 markdown files (DB-backed) | 426 markdown files | | Audience cap | **none** | `guide.served-audiences` | Until this lands, anyone building hiveops-guide from `main` — including `cd-main` if it ever starts running — gets an empty Guide with **no audience gating at all**. That is the main risk this PR retires. ## Security: the cap now fails safe (`451c475`) The environment cap decided what an instance serves, but **defaulted to all four tiers**. That made the safe configuration the one you had to remember — a missing or misspelled `GUIDE_SERVED_AUDIENCES` in production would have served the **Architect and Claude tiers to any MSP_ADMIN**, and MSP_ADMIN is a customer-facing role (QDS log in as MSP_ADMIN). One forgotten env line, same class as the feedback import that exposed internal Security tickets to QDS. Default is now **`customer`**. Environments opt UP explicitly: | Environment | `GUIDE_SERVED_AUDIENCES` | Serves | |---|---|---| | **Production** | *(unset — default)* | `customer`: modules + Customer tab (68 of 426 docs) | | **bcos.dev** | `customer,internal,dev,bcos` | all four tiers | bcos.dev was pinned to all four tiers **first** (`hiveiq-openmetal-dev` `b2d4e00`, already live on `.251`) so this cannot narrow it. `platform.guide-access` is updated too — it documented the old default and told readers the opposite of what the code now does. Verified in code: the cap is checked in `visible()` **before** any role logic, on both read paths (`nav()` and `module()`). Role can narrow the set, never widen it past the cap. Caveat recorded in the docs: the cap governs what is *served*, not what is *present* — all content still ships in the image. ## What is in it 84 commits, 2026-06-30 -> 2026-07-16: - **Markdown content model** — 426 docs across 125 modules; no database (the `hiveiq_guide` DB is a fossil of the old design and is unused). - **Per-environment audience cap** — `guide.served-audiences` (`customer < internal < dev < bcos`), enforced server-side in `MarkdownGuideService`. - **Audience gating fixes, develop-only until now:** - `f21c783` — gate Architect + Claude tabs to BCOS_ADMIN only (#16) - `4f84fc5` — dev tier BCOS_ADMIN-only; keep Testing for MSP (#18) - **Security** — `7174229` bump hiveops-bom 1.0.5 -> 1.0.7, clears httpcore5 + postgresql CVEs. - **CI** — `e1fcd4c` Slack notify made non-fatal. - Content: MILESTONE nav section, devops.prod/devops.dev (BCOS-only), fleet.task-permissions (#23), plus ~30 docs commits. ## Notes for the reviewer - **No CI runs against this PR.** hiveops-guide has only `cd-develop.yml`; there is no `cd-main`, so this merges on approval alone with no automated check (devops#46). - **Version goes backwards on main**: `4.0.0` -> `1.0.0`. The rewrite reset the lineage. Harmless while `build.sh` tags `:latest`, but worth a conscious nod. - No `-SNAPSHOT` in the pom. ## Known follow-up — not fixed here `GuidePanel.svelte` in **hiveops-incident** still calls the OLD contract (`/guide/api/v1/guides/{pageKey}`, keys `incidents.list` / `incidents.create`). That is the root cause of feedback #788 ("Guide button not working for MSP") — an unmigrated consumer, not a typo. It needs repointing to `/guide/api/v1/guide/modules/{module}` with `incident.*` keys. **The Guide button stays dead until that lands, deploy or no deploy.** 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hiveiq added 83 commits 2026-07-16 15:08:07 -04:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the guide rebuild (DESIGN.md):
- MarkdownGuideService scans content/**/*.md (frontmatter: module/title/tab/order/audience)
- GET /api/v1/guide/nav + /api/v1/guide/modules/{module}; audience gated by role
  (internal tabs only for MSP_ADMIN/BCOS_ADMIN)
- Removed JPA/Flyway/Postgres, Guide entity/repo/service, admin CRUD, seed migrations
- Seed content: devices.sw-deploy (Overview/Data/Technical-internal), fleet.tasks
- Verified: boots with no DB, loads 4 docs, nav + audience filtering + 401 all correct

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge PR #2: markdown-driven guide backend (Refs #1)
Ported from the previously-hardcoded GuidePanel objects in hiveops-fleet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Internal (audience:internal) guides capturing the HiveIQ architecture that caused
repeated mistakes: data-architecture (executor/Kafka/record-store), service-ownership,
deployment topology, auth/JWT, git workflow, module map. For admins + Claude's reference.
Cross-cutting internal guides so admins/Claude can query HiveIQ topology,
routing, data ownership and Kafka flows without re-reading code. All
audience: internal, marked DRAFT pending review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One code-grounded internal guide per backend service (Responsibility / Data /
Kafka / API surface / Gotchas), authored by parallel per-service agents reading
each service's own source, then an adversarial verify pass (DB names, topics,
endpoints spot-checked against code — 0 hard errors). Several guides correctly
document CODE over stale CLAUDE.md (remote = HTTP long-poll not WebSocket;
analytics = has a DB, not stateless; devices = has its own backend).
Unconfirmed facts are marked (unverified). audience: internal, all DRAFT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The per-service pass surfaced 10 topics beyond the original 5 (agent.online/
offline, fleet.task.events, incidents.created/updated/resolved, journal.
transactions.recorded, analytics.snapshot-ready, adoons.rule-suggestion.
requested/ready). All grep-confirmed as literals in producing-service source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New GuideReader.svelte: nav tree (apps→modules) + tabbed markdown (marked),
audience-gated server-side (admins see internal tabs, badged). Replaces the old
DB-CRUD GuidesView. Consumes GET /api/v1/guide/nav + /modules/{module}.
Deployed to guide.bcos.dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 plain-language audience:customer Overview guides, one per primary
user-facing module (analytics/aria/claims/dashboard/devices/incident/msp/
profile/recon/reports/transactions/vault + fleet), authored by a per-module
parallel pass grounded in each component's actual UI, mirroring the fleet.tasks
voice. Existing hand-made guides were skipped, not overwritten. Deployed to
guide.bcos.dev for review; all DRAFT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
authApi baseURL is .../auth, so posting /auth/api/login double-prefixed to
/auth/auth/api/login → 403. Use /api/login and /api/users/me.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Login page: dark card/inputs, Show/Hide password toggle (manual value bind
since dynamic type can't use bind:value). Reader: dark content, tabs, and
markdown (headings/tables/code/blockquote).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reader shows role-based tabs with icons (order 10/20/30/40, internal gated).
Fleet pilot: relabel Customer tab + author Internal/Architect/Claude tabs for
tasks/artifacts/patch-windows, grounded in fleet code. Architect/Claude tabs
surfaced real bugs (patch-window day-code, /stats hardcoded counts) — flagged
separately, not fixed here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full role-tab rollout: every customer module now has Customer/Internal/
Architect/Claude tabs, grounded per-module in real code. Backend serves 296
docs / 96 modules. Grounding surfaced ~20 real bugs across services (filed
separately after verification).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restructure reader sidebar into 3 collapsible top-level sections: Modules at
top (each product app collapsible with its module list), then Platform and
Technical. Auto-expands the section/app of the active module; search expands all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
isOpen() helper hid the dependency on `open` from Svelte's tracking, so clicks
updated state but not the view. Inline (searching || open[key]) instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New Testing tab (order 35, audience internal, 🧪): test accounts, manual flow,
  copy-paste API smoke tests, role/scoping checks, regression watch. Fleet pilot.
- Reader shows 'Signed in as {email} · {role}' above Sign out (decoded from JWT),
  so you always know which identity you're testing as.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per Johannes: Testing tab is a tester's what-to-test -> pass/fail checklist (UI
only, bcos.dev sim data C1/C2/C3 + test logins), not an engineering API spec.
Dropped curl/endpoints/backend. New pattern for the fan-out.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every customer module now has the full 5-lens set (Customer/Internal/Architect/
Testing/Claude). Testing = frontend-only what-to-test -> pass/fail checklist vs
bcos.dev sim data (C1/C2/C3 + test logins), audience internal. Authored via
per-module fan-out (2 retried on Sonnet 5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New 3-tier gating: customer < internal (MSP/BCOS) < bcos (BCOS_ADMIN only).
GuideController.isBcos + service visible() honour audience: bcos. New 'processes'
app with Develop / Main / Release / Gitea-Issue process docs (audience bcos),
rendered as a Processes nav section only BCOS_ADMIN can see. Verified: BCOS 200,
MSP/customer 404.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ties in dlx-claude's gitea-notify webhook: closing a Gitea issue DMs the reporter
in their HiveIQ inbox using the Resolution field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend FeedbackController + GiteaFeedbackService (POST /api/v1/guide/feedback):
resolves/creates the Bug/Enhancement label, appends reporter (from JWT) + module
context, POSTs the issue via a server-side GITEA_FEEDBACK_TOKEN (reaches Gitea via
public route). Area→repo map mirrors the Browser. Reader gets a 💬 Send feedback
button + dark slide-in form (Bug/Enhancement, area prefilled from current module,
title, description) with success link. Verified e2e (created hiveops-guide #4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4-tier ladder customer<internal<dev<bcos. Re-tagged Architect/Testing/Claude +
platform/technical from internal->dev; Processes stays bcos. Backend visible()
enforces a per-instance served-audiences cap at the API. bcos.dev/DLX serve all;
production sets GUIDE_SERVED_AUDIENCES=customer,internal so dev/bcos never leave
dev. Verified: capped instance returns only Customer+Internal, 404s dev/bcos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
scripts/coverage-reconcile.py diffs the code surface (services, agent modules,
SPAs) against documented module keys and reports gaps — catches undocumented
things (found 15 agent modules + qds-monitor). Also relabeled 63 customer tabs
Overview→Customer for role-tab consistency with the fleet pilot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the agent-module coverage gap the reconciler flagged (activeteller + 14).
Grounded per module in hiveops-agent/hiveops-module-*; audience dev (dev-only).
Reconciler now green. Grounding surfaced agent-side bugs (log-error route
mismatch, atec poll-interval hardcoded) — to verify + file next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New platform.ci-cd page: runner topology (dlx-hiveiq-runner-01), no-internet
design, workflow set, access, and failure signatures. gotchas: correct the
stale \"CI is disabled\" line and add the AdGuard DNS SPOF (hits app boxes + CI
+ Claude sessions). Grounded in runner logs from the 2026-07-01 outage.

Refs hiveiq-ops/hiveiq-devops#11

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The activeteller agent module now emits two connectivity-recovery events
(hiveops-agent#68) that drive incident auto-close. Adds them to the event lists
(internal + claude), bumps the count 9→11, and corrects the SymXchange behaviour
in architect.md: the connection-error streak resets (and IAT_CORE_RECOVERED fires)
on the next message header, not on a fault line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- platform.production-checks: the daily prod health sweep (deep actuator/critical
  set, Loki ERROR+5xx top-5, backup freshness, ClamAV per-VM) -> 06:30 email report.
- NEW DevOps nav section in the reader (SPECIAL + section block + expandFor).
- devops.backups: pg_dumpall->Swift nightly job, verify/restore, gaps (#13/#14/#15).
- devops.security: ClamAV/firewall posture from the 2026-07-02 sweep — FAILING,
  remediation in #16 (Critical, PCI CDE) / #17. Reconciler: devops = concept docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First pass had a partial view. Backups are 3 layers (Swift dumps + encrypted
off-cluster borg to DLX + Cinder snapshots), not just pg_dumpall — #15 (DR)
resolved, #13/#14 re-scoped. Security has iptables auto-block (security-monitor)
+ AIDE FIM — #17 re-scoped; ClamAV (#16) remains the one real gap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
devops.security: #16 remediated (freshclam + clamscan, no resident clamd); 5/6
VMs done incl CDE; proxy EXCLUDED (957MB, install OOM d it -> hard reboot).
platform.gotchas: check VM RAM before installing memory-heavy tools.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The redirect (>> /var/log/x.log) fails before the command runs when the target
is root-owned/absent; job dies with zero output. Caused backups #18/#19 + IDS
#22 outages. Diagnose via redirect-target owner; fix with chown + logrotate create.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SGs are the firewall of record — public surface 80/443, SSH/Loki restricted to
internal+admin. All VMs key-only (passwordauthentication no) -> brute-force
mitigated, fail2ban optional. Dead SSH-ingress SG (22->world, unattached) to remove.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Boot 3.5.14 + netty/tomcat/jackson/spring-kafka/spring-cloud/spring-ai/commons/
bcprov overrides. Validated via Phase-1 fan-out: builds clean, backend Trivy 0
HIGH/CRITICAL. (Frontend npm axios/form-data handled separately where present.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a Milestones section to processes.gitea-issues: releases are tracked by per-repo
YYYY-MM milestones (not labels), Next = milestone with no due date, Backlog = no
milestone. Note the retired Release/* labels, cross-repo rollup via issues/search, and
assignment API. Cross-link from processes.release for release scoping.
Document the MSP_MANAGED/CUSTOMER_MANAGED incident ownership mode across the
Claude/Internal/Architect/Testing/Customer tabs: the write matrix, the
/institution-keys/{key}/incident-mode endpoint + MSP toggle, controller-layer
assertWritable enforcement, IncidentDTO.institution, V90 migration, and the
known UI follow-up (customer nav still hidden).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
platform.nginx-routing: replace the stale 'flip map + internal callers in sync' guidance with the
new pattern — internal callers point at hiveiq-nginx:<port> and follow the 00-backends.conf map
(10-internal-backends.conf). Document the targeted-flip gotcha (global sed corrupts the derived
active_color table; bluegreen-deploy.sh has this bug) and 'never recreate the active color'.
platform.deployment: add a prod blue/green cutover section + the 2026-07-03 incident lesson.

Refs hiveiq-ops/hiveiq-devops#27, hiveiq-ops/hiveiq-openmetal-prod#20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clears 2026-07 NVD batch (spring-framework 6.2.19 / spring-security 6.5.11 / tomcat 10.1.56).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
processes.gitea-issues: document the backfilled release timeline — 2026-02/03 pre-pilot,
2026-04/05 pilots, 2026-06 production go-live (17 June), 2026-07 current. Notes the
close-date scoping, the 100%-closed historical milestones, and the search-API pagination
gotcha (newest-first, ~200 cap).
The guide bundles content into the backend jar at build time, so instances must be rebuilt +
redeployed to pick up doc changes (git-pull is insufficient). Automates it: on push to develop
(backend/** paths), mvn package -> docker build -> push :dev -> deploy to bcos.dev -> smoke.

mvn package runs FIRST (the Dockerfile COPYs a pre-built jar — skipping it ships stale content).
Auto-deploy scoped to dev only (docs, low risk). Audience filtering is runtime via
GUIDE_SERVED_AUDIENCES, so no build-time filtering — prod (future cd-main, gated) serves
customer,internal only.

Needs (coordinate w/ CI/runner session): BCOS_DEV_DEPLOY_KEY secret + runner reachability to .251.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci(guide): note BCOS_DEV_DEPLOY_KEY provisioned — trigger deploy verification
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 59s
7bc96eeaa7
QDS_ADMIN was removed from the hiveops-auth Role enum (972a28a); no JWT can carry
ROLE_QDS_ADMIN. Strip the stale SecurityConfig hasAnyRole entry. Zero runtime change.

Refs hiveiq-src/hiveops-incident#253

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Appends a Notify Slack step to cd-develop (if: always()) that posts a
colored Block Kit card on every run —  deployed / 🔴 FAILED — with commit,
branch, actor, run number, and a View-run button. Uses the org secret
SLACK_WEBHOOK_URL; no-ops if the secret is unset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reword journal Overview to drop the 'legacy SmartJournal dependency' mention
and remove smartjournal from the coverage-reconcile deprecated skip-list.
qds-monitor stays in the skip-list (deprecated, intentionally undocumented).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Notify Slack step could fail the whole job (turning a successful deploy
red, as just happened). A notification must never fail a deploy:
- continue-on-error: true on the step
- curl captures HTTP code and never propagates a non-zero exit (|| echo)
- logs "slack notify HTTP: <code>" so we can see if the runner reaches Slack
- first commit line via ${VAR%%$'\n'*} param-expansion (no head pipe)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to hiveops-incident#253 (QDS_ADMIN removed from all 5 backends'
SecurityConfig/InstitutionContext, and from the hiveops-auth Role enum so no
JWT can carry it). The guide KB still quoted QDS_ADMIN in role/allow-list
descriptions, which no longer matched the code.

Removed QDS_ADMIN from 26 references across 23 content files; surviving roles
(USER, CUSTOMER, ADMIN, MSP_ADMIN, BCOS_ADMIN, AGENT, SYSTEM) left unchanged.
No frontmatter touched -> zero module-coverage impact. Doc-accuracy only.

Closes #10

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The smoke step polled /guide/actuator/health for only 60s, but the Spring
Boot backend restart after `compose up -d` needs longer to warm up — so a
successful deploy was marked FAILED (run #6, PR #11). Poll 30x10s (~5 min)
and echo attempts. No change to the deploy itself.

Closes #12

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): add platform.frontend-template module (#14)
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 31m32s
b047679927
Guide had no coverage for hiveops-template, so the copy-don't-rewrite rule
and the TemplatePage-vs-TablePage decision were only in a stale CLAUDE.md.

- decision table for which page to copy; ask the user when not obvious
- assigned dev ports read from the actual vite configs (next free = 5191;
  CLAUDE.md still claims 5180, which is adoons)
- canonical .page-right spacing is 0.65rem/0.5rem, not the documented 1rem/0.75rem
- dark-mode filter-header values + collapsed-by-default sidebar

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): add processes.customer-provisioning — wizard runbook + handoff gotchas
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 1m4s
7429f1413b
Documents the mgmt portal 5-step New Customer + Institution wizard shipped to
production in hiveops-mgmt#43: the five steps, atomic-submit semantics, the
MSP_ADMIN agent-onboarding handoff message, and the gotchas (uppercased
institution key, unrecallable handoff, skipping step 4 skips the handoff,
MESSAGING_SERVICE_URL required).
guide: document supervisor/service-state mirror (devices#121)
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 36m22s
ffc06e2ef5
incident.journal-events (Claude tab): the state flip is a dual-write — incident
writes its own atm_properties, then mirrors to devices, and it is devices the UI
reads. Adds the best-effort/afterCommit semantics and the nginx-listener URL rule.

technical.devices: document POST /api/internal/atms/{agentAtmId}/service-state,
that devices does not ingest journal events, the now()-stamped supervisor_entered_at,
and that the status badge is derived client-side (agent-offline masks IN_SUPERVISOR).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): add release-prep milestone-viewing workflow to processes.gitea-issues
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 1m13s
33ed88e07c
Document how to view a release milestone's issues in Gitea:
- per-repo web UI filter URL pattern (milestone=<id>)
- cross-repo rollup via issues/search by name
- a copy-paste one-liner that regenerates the current-month
  per-repo links for any YYYY-MM (no hardcoded ids)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(guide): add MILESTONE nav section + 2026-07 release-scope page
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 1m2s
b2fe064e7d
- New top-level MILESTONE section (bcos-only), peer to PROCESSES, renders
  uppercase via .sec-head; first module is milestone.2026-07 (per-repo
  release-scope view with Gitea filter links).
- Nav UX: all sidebar sections start collapsed; no section/app auto-expands
  on entry; landing pane shows the HiveIQ logo instead of auto-selecting Agent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(guide): correct 2026-07 milestone data + open external links in new tab
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 32m54s
b70c4acda4
- 2026-07 page: authoritative counts from org-wide milestone-name search
  (38 open across 12 repos; add devices=4, fleet #50, dashboard #51 that the
  stale per-repo milestone counters had hidden).
- External http(s) links in rendered guide content now open in a new tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): add per-repo All (open+closed) link column to 2026-07 page
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 35m16s
9abcd0aa5a
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): remove 'Everything at once' section from 2026-07 page
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 1m5s
737c7001ae
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): 2026-07 page columns = Open(no milestone) + 2026-07(open), with counts
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 34m48s
2d0475ce6f
Replace Open/All columns with: open issues with no milestone (unscheduled
backlog) and open issues in 2026-07; linked number is the count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OWASP now fails builds on three HIGH CVEs in transitive deps. bom 1.0.7 takes the upstream patches
(no suppressions):

  postgresql 42.7.11 -> 42.7.13   CVE-2026-54291 (CVSS4 8.2) — channelBinding downgrade / MITM
  httpcore5  5.3.6   -> 5.4.3     CVE-2026-54399 (7.5), CVE-2026-54428 (7.5) — DoS

httpcore5 is transitive via httpclient5; Apache ships them as a matched pair, so bom 1.0.7 bumps
httpclient5 5.5.2 -> 5.6.2 which pulls the fixed core.

Verified locally: builds and tests pass on 1.0.7.

Refs bom#6

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): add devops.prod + devops.dev (BCOS-only), fix stale ops script paths
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 35m7s
0068ea0a14
Two new BCOS-only modules documenting the two environments and the rule that
separates them:

  devops.prod  Production Environment (OpenMetal) — hosts, repo layout, health
               report + config drift, and the KNOWN DRIFT warning: main's
               00-backends.conf names different active blue/green slots than prod
               actually serves (devices/transactions/agent-proxy), so deploying it
               as-is flips three services to the wrong slot. Do NOT bulk-copy main
               over prod.

  devops.dev   Dev Environment (bcos.dev) — single 16G all-in-one VM at ~93-97%
               RAM (recreate JVM services ONE at a time), its own postgres and
               registry, the new 06:00 health report (johannes only), and the
               health-check gotchas that must not be "fixed" back: SPA frontends
               return 200 for /actuator/health so the body must be checked, the
               probe must retry, and exited *-green/*-blue are standby slots.

Both audience: bcos.

The rule, now stated in both:
  everything bcos.dev  -> hiveiq-openmetal-dev
  everything prod      -> hiveiq-openmetal-prod
  shared, env-agnostic -> hiveiq-devops

Also corrects devops.backups and devops.security, which still told readers the
backup/security scripts live in `hiveops-devops/scripts/ops/`. They were moved to
`hiveiq-openmetal-prod/scripts/ops/` today — the Guide is the source of truth, so
a stale path in it is worse than none.

Refs: hiveiq-ops/hiveiq-devops#39, #38

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): title devops.prod by its domain — Production Environment (bcos.cloud)
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 31m37s
1727f3309b
Mirrors devops.dev 'Dev Environment (bcos.dev)'. The domain is what people
actually navigate by; OpenMetal is the hosting provider, which is an
implementation detail and is already covered in the body.
docs(guide): CI builds images, not you — correct deployment + release + ci-cd
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 32m40s
8d7450d747
platform.deployment and processes.release both walked the reader through ./build.sh, never
mentioning that cd-develop/cd-main already build and scan. Following either one produces an
UNSCANNED image that races CI for the mutable :dev tag. That is what happened on 2026-07-13:
a full day of hand-built images reached bcos.dev without passing Trivy.

Verified against the workflow YAML and both runners on 2026-07-14:

- cd-develop does NOT deploy for 6 of 8 repos. Only incident and devices deploy to bcos.dev;
  everywhere else CI stops at push. The old 'a green run redeploys the service' claim was wrong.
- hiveops-fleet CI builds the FRONTEND ONLY — the fleet backend is built by neither cd-develop
  nor cd-main. hiveops-devices cd-main builds only the frontend too. Flagged as defects.
- runner-02 IS active (incident built on it today) — the 'not the active worker' note was wrong.
- CI now pushes an immutable :<sha8> alongside :dev/:latest, so deploys can pin a scanned image.

Also records the runner failure signatures ('lease does not exist', 'network ... not found'),
the daemon-restart + prune runbook, the standing warning never to garbage-collect or delete
/opt/docker-registry, and the fact that merging a batch of PRs at once overwhelms a runner's
4 shared job slots and breaks its Docker daemon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): correct a false claim I just added — cd-main does NOT run, production is manual
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 33m24s
74bcb97ecb
The previous commit (8d7450d) said CI builds production images on merge to main. That is WRONG.

cd-main.yml exists in most repos but is dormant scaffolding: it targets runs-on: production, and
the only runner advertising that label (runner-02) is not an enabled service. ZERO cd-main runs
have ever executed in any repo. Merging to main builds nothing.

Only cd-develop runs. Production is built by hand, on purpose:
  develop -> bcos.dev  = CI builds + scans. Never hand-build.
  main    -> production = you build by hand. ./build.sh is CORRECT here.

Consequence now stated explicitly in both modules: the npm-audit + Trivy gate exists ONLY on
develop, so production images are unscanned by construction. That is why 'ship only what has been
verified on bcos.dev' is the entire security argument, not a nicety.

Also corrects the fleet gap to what it actually is — cd-develop never builds hiveiq-fleet — and
drops the devices cd-main 'gap', which was not a gap because that pipeline does not run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): cash-stats per-terminal grand total (dashboard#15)
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 1m39s
e79dd89362
Documents the Total shown under each ATM name on Cash Stats, across all five
tabs.

The load-bearing facts, which are easy to get wrong:
- The total is derived client-side in CashStatus.svelte. No endpoint, DTO, or
  column backs it, and none should be added.
- The asterisk means the total is a floor, not a balance: mix/retract notes
  (no denomination) and cassettes with no reported count are excluded. An empty
  retract cassette deliberately does not flag the row.
- An ATM with no reported inventory shows a dash, never $0, which would read as
  an empty machine.
- The pills must keep wrapping: real fleets run 5+ cassettes, which under the
  old nowrap layout overflowed the table and crushed the ATM column.

Also notes that the bcos.dev simulator emits no mix/retract slots, so the
partial-total path never fires there and must not be mistaken for dead code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(guide): SPA smoke tests use hiveops-e2e (Playwright)
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Failing after 34m48s
32bc8e7440
"Deploy = smoke test" only told you to curl the endpoint. An SPA returns 200 + index.html for any
path, so a curl proves nothing about a frontend feature — which is how dashboard#15 got called
"verified" on the strength of a container healthcheck and a bundle grep.

Documents hiveiq-src/hiveops-e2e and, more importantly, the trap in front of it: these SPAs cannot
be driven by a plain browser. They need X-HiveIQ-Browser: true (nginx's browser gate 302s anything
else to the marketing site) and Authorization: Bearer <jwt> (the SPAs carry no auth code; the
Electron shell injects it).

The headers must be injected at the network layer, not via Playwright's extraHTTPHeaders — those
land in the CORS preflight, nginx rejects X-HiveIQ-Browser there, and every XHR fails. The app then
renders and says "Failed to load...", which reads as a broken feature and is not.

States plainly that coverage is one spec, so nobody assumes the suite has them covered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 162 Architect/Claude docs were tagged 'audience: dev'. The 'dev' tier
resolves to isInternal() = ROLE_MSP_ADMIN || ROLE_BCOS_ADMIN, so every
MSP_ADMIN could read full internal architecture: source paths, DB tables,
service ports, NGINX routes. Verified live on bcos.dev as msp_a@msp1.bcos.dev.

Retag them to 'audience: bcos' (isBcos, BCOS_ADMIN only). Content-only change;
the 'dev' tier mapping is deliberately left alone so the 15 Internal, 35
Overview and 66 Testing docs stay MSP-visible.

Also add platform.guide-access documenting the audience tiers and the
per-role visibility matrix, so the access model is queryable from the Guide
instead of re-derived from source each time.

Verified: MSP module count unchanged (116 across 17 apps); no module loses
all its tabs. Guide is bcos.dev only, not deployed to production.

Closes #16

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 'dev' audience tier was gated via isInternal() (MSP_ADMIN || BCOS_ADMIN),
so MSP admins could read developer/architecture content: technical.* and
platform.* Overviews, agent-module internals, dev Internal/Overview tabs.

Remap 'dev' to isBcos() (BCOS_ADMIN only) in visible(). Retag the 66 Testing
docs from 'audience: dev' to 'audience: internal' so MSP keeps them, per
Johannes.

Result: MSP sees Customer + Internal + Testing (66 modules); loses all
Architect/Claude/architecture. BCOS unchanged. Update platform.guide-access
to the new model.

Closes #18

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the msp.institution-keys module (Customer/Internal/Claude/Testing tabs)
and technical.devices to match the new authz:
- Only BCOS_ADMIN can create/delete institution keys; MSP admins edit only and
  must submit a request to BCOS to add/remove an institution.
- POST/DELETE /api/institution-keys -> hasRole('BCOS_ADMIN'); PUT stays MSP+BCOS.
- Note the underlying ownership gap (devices mints institutions with no mgmt
  mirror; keys have diverged) tracked in msp#29.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document that the flow normalises host_result (SUCCESS->approved, FAIL/TIMEOUT
->declined) and surfaces a decline Reason, matching hiveops-transactions #49.
Note the note-breakdown still needs raw_lines and a structured reason field
remain follow-ups on #48.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Behaviour changed in hiveops-auth#56 / hiveops-profile#10 (both live on
bcos.dev), so the Guide has to follow.

profile.notifications
- inboxPollSeconds added to the prefs doc, defaults and validation tables
  (0-3600, 0 = off, default 60; no migration, absent key reads as default)
- corrects the "flags are stored only, no consumer" claim: it is still true
  of email/push/severity/quiet-hours, but inboxPollSeconds now has a real
  consumer (App.svelte arms the poll from it). It is the only setting on
  that tab that does anything today
- Customer tab documents the new Messages section; testing checklist gains
  rows for the interval, Off, badge appearing, and badge clearing
- internal: 400 cause, Off-by-design and silent-poll-failure rows

profile.inbox
- documents the sidebar badge: stores.ts owns unreadCount/refreshUnread,
  poll interval is an auth-owned pref, unread-only semantics, #ef4444 vs
  the sub-tab's #2563eb, and why it must not reuse Inbox's page-local tally

Refs hiveops-auth#56, hiveops-profile#10

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Task Permissions tab shipped in hiveops-fleet#119 and is live, but had no
guide module at all — the API returned 404 for fleet.task-permissions, and
fleet.tasks mentions permissions once without describing the grant model.

This is the setting that decides which commands a customer may push to their own
ATMs: deny-by-default, per-institution, per-command. Highest-consequence fleet
setting we have, least documented.

Adds the 5 canonical role tabs, grounded in hiveops-fleet source:

- Customer  — grant/revoke, why only Reboot + Restart Agent are offered
- Internal  — triage for "customer cannot reboot", what we never widen
- Architect — deny-by-default rationale, the two independent gates, replace-not-
              merge semantics, why GRANTABLE lives in code
- Testing   — role matrix + the 400-on-CONFIG_UPDATE case, against bcos.dev
- Claude    — ownership, table, endpoints, gotchas

Verified: all 5 tabs parse under MarkdownGuideService.parse(); reconciler counts
126 modules with no missing role-tabs. Role matrix confirmed live on bcos.dev
(CUSTOMER 403/403/200/403, MSP_ADMIN 200/200/200/200).

Refs #23. Follow-up to hiveops-fleet#119, hiveops-fleet#100.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
johannes approved these changes 2026-07-16 15:08:58 -04:00
Dismissed
hiveiq added 1 commit 2026-07-16 15:19:36 -04:00
fix(guide): default served-audiences to customer — fail safe, not fail open
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 2m18s
451c475b91
The environment cap decided what a Guide instance serves, but defaulted to ALL
four tiers. That makes the safe configuration the one you have to remember: a
missing or misspelled GUIDE_SERVED_AUDIENCES in production would serve the
Architect and Claude tiers to any MSP_ADMIN — and MSP_ADMIN is a customer-facing
role (QDS log in as MSP_ADMIN). One forgotten env line was all it took.

Default is now `customer`. Environments opt UP explicitly:
  production   -> unset (customer only: modules + Customer tab)
  bcos.dev/DLX -> customer,internal,dev,bcos (set in that instance's compose)

bcos.dev's compose was pinned to all four tiers first (hiveiq-openmetal-dev
b2d4e00, already live on .251) so this change cannot narrow it.

Also updates platform.guide-access, which documented the old default and told
readers the opposite of what the code now does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
johannes approved these changes 2026-07-16 15:23:16 -04:00
hiveiq merged commit d8e4d9f3d1 into main 2026-07-16 15:24:26 -04:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: hiveiq-src/hiveops-guide#27