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>
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>
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>
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>
"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>
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>
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>
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>
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.
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>
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>
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>
- 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>
- 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>
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>
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>
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 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>
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>
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>
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>
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>
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>
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.
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>