# CLAUDE.md — hiveiq-ops Operations repo for the HiveIQ/HiveOps platform. Split from `hiveops-src` (application source) to separate deployment configs, infrastructure, and documentation from service code. **Application source:** `/source/hiveops-src/` **Ops source (this repo):** `/source/hiveiq-ops/` --- ## Sub-Projects | Directory | Purpose | |-----------|---------| | `hiveiq-devops/` | Docker Compose + Kubernetes + Ansible deployment configs — see `hiveiq-devops/CLAUDE.md` | | `hiveiq-openmetal/` | **Production** OpenMetal cloud infrastructure (bcos.cloud) — see `hiveiq-openmetal/CLAUDE.md` | | `hiveiq-openmetal-dev/` | **Dev** OpenMetal environment (bcos.dev) — docker-compose, nginx, `.env.example` | | `hiveiq-docs/` | MkDocs documentation site — `mkdocs.yml` (main) + `mkdocs-qsa.yml` (QSA audit) | **Production infra (bcos.cloud):** `hiveiq-openmetal/CLAUDE.md` **Dev environment (bcos.dev):** `hiveiq-openmetal-dev/DEV-ENVIRONMENT-SETUP.md` **Start here for deployment configs:** `hiveiq-devops/CLAUDE.md` --- ## Releasing a New Agent Version The agent build lives in `/source/hiveops-src/hiveops-agent/` but the release deploys to CDN (managed here in `hiveiq-openmetal/` — production only). ```bash # 1. Bump version in source repo cd /source/hiveops-src/hiveops-agent mvn versions:set -DnewVersion=X.Y.Z -DgenerateBackupPoms=false # 2. Build fat JAR mvn clean package -DskipTests # 3. Build dist packages — run sequentially (build-dist.sh clears dist/ each run) deployment/build-dist.sh --release --platform windows scp -i ~/.ssh/id_ed25519_hiveiq deployment/dist/hiveops-agent-X.Y.Z-standard-windows.zip bcosadmin@173.231.195.252:~/hiveiq/hiveops-openmetal/hiveops/instances/browser/downloads/agent/ deployment/build-dist.sh --release --patch --platform windows scp -i ~/.ssh/id_ed25519_hiveiq deployment/dist/hiveops-agent-X.Y.Z-patch-windows.zip bcosadmin@173.231.195.252:~/hiveiq/hiveops-openmetal/hiveops/instances/browser/downloads/agent/ deployment/build-dist.sh --release --platform linux scp -i ~/.ssh/id_ed25519_hiveiq deployment/dist/hiveops-agent-X.Y.Z-standard-linux.tar.gz bcosadmin@173.231.195.252:~/hiveiq/hiveops-openmetal/hiveops/instances/browser/downloads/agent/ deployment/build-dist.sh --release --patch --platform linux scp -i ~/.ssh/id_ed25519_hiveiq deployment/dist/hiveops-agent-X.Y.Z-patch-linux.tar.gz bcosadmin@173.231.195.252:~/hiveiq/hiveops-openmetal/hiveops/instances/browser/downloads/agent/ # 4. Update CDN manifest — pass all 5 args so patches appear in Fleet › Artifacts ssh -i ~/.ssh/id_ed25519_hiveiq bcosadmin@173.231.195.252 cd ~/hiveiq/hiveops-openmetal/hiveops/instances/browser ./scripts/release-agent.sh X.Y.Z \ "hiveops-agent-X.Y.Z-standard-windows.zip" \ "hiveops-agent-X.Y.Z-standard-linux.tar.gz" \ "hiveops-agent-X.Y.Z-patch-windows.zip" \ "hiveops-agent-X.Y.Z-patch-linux.tar.gz" # 5. Commit version bump + push in source repo cd /source/hiveops-src/hiveops-agent git add -A && git commit -m "chore(agent): bump version to X.Y.Z" git push # 6. Update VERSIONS.md in hiveops-src ``` **Bearer token revocation rule:** before revoking any `agent.auth.token`, push a CONFIG_UPDATE fleet-wide and wait for all tasks to drain. Revoking while ATMs still hold the token kills module threads — recovery requires a manual JVM restart on every affected ATM. --- ## Updating "What's New" Release Notes **Repo:** `/source/hiveops-src/hiveops-release/` (separate source repo, deploy via script) ```bash cd /source/hiveops-src/hiveops-release # 1. Edit releases.json — prepend new entry to the relevant service array # 2. Deploy ./deploy.sh # 3. Verify # https://cdn.bcos.cloud/whats-new?app= ``` ### Service keys in releases.json | Key | Service | |-----|---------| | `browser` | HiveIQ Browser (Electron) | | `incident` | Incident web app | | `fleet` | Fleet web app (`fleet.bcos.cloud`) | | `devices` | Devices web app | | `transactions` | Transactions web app | | `analytics` | Analytics web app | | `agent` | HiveIQ Agent (ATM JAR) | | `journal` | Journal microservice | | `rules` | Journal parsing rules | | `reports` | Reports microservice | Entry format: ```json { "version": "v1.2.0", "title": "Short descriptive title", "date": "May 10, 2026", "sections": [ { "type": "added", "items": [ { "title": "Feature name", "note": "One sentence describing what changed and why." } ] } ] } ``` Section `type` values: `added`, `fixed`, `improved`, `changed`, `removed`. --- ## share.bcos.cloud — Customer File Distribution Used to distribute patches that can't be auto-updated (e.g. ATEC Java 8 branch with `agent.update.enabled=false`). **VM:** `bcos-share` — `173.231.195.254` **Login:** `hiveiq@bcos.cloud` Pingvin Share v1.13.0 reads the JWT from **cookies**, not the `Authorization` header. ```bash # 1. Sign in curl -s -c /tmp/pingvin-cookies.txt -X POST https://share.bcos.cloud/api/auth/signIn \ -H "Content-Type: application/json" \ -d '{"email":"hiveiq@bcos.cloud","password":""}' # 2. Create share curl -s -b /tmp/pingvin-cookies.txt -X POST https://share.bcos.cloud/api/shares \ -H "Content-Type: application/json" \ -d '{"id":"my-share-id","expiration":"never","recipients":[],"description":"..."}' # 3. Upload file (chunk params as query string, body is raw binary) curl -s -b /tmp/pingvin-cookies.txt -X POST \ "https://share.bcos.cloud/api/shares/my-share-id/files?chunkIndex=0&totalChunks=1&name=filename.zip" \ -H "Content-Type: application/octet-stream" \ --data-binary "@/path/to/file.zip" # 4. Complete curl -s -b /tmp/pingvin-cookies.txt -X POST \ https://share.bcos.cloud/api/shares/my-share-id/complete ``` Share URL: `https://share.bcos.cloud/s/{shareId}` --- ## HiveIQ API — Service Account Login ```bash LOGIN=$(curl -s -X POST "https://api.bcos.cloud/auth/api/login" \ -H "Content-Type: application/json" \ -d '{"email":"hiveiq@bcos.cloud","password":"HiveIQ-Admin-2026!","rememberMe":false}') JWT=$(echo "$LOGIN" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('token',''))") ``` - Response field is `token` (not `accessToken`), signed HS512 - Route API calls through NGINX: `https://api.bcos.cloud//api/...` - Login body uses `email` field (not `username`) --- ## Common Operational Mistakes ### PostgreSQL is on a SEPARATE VM The `hiveiq-postgres` container runs on **`10.10.10.188`** (hiveiq-database VM), NOT on `173.231.195.250` (services VM). ```bash # CORRECT — ProxyJump to database VM ssh -i ~/.ssh/id_ed25519_hiveiq -J bcosadmin@173.231.195.250 bcosadmin@10.10.10.188 \ "docker exec hiveiq-postgres psql -U hiveiq -d hiveiq_journal -c 'SELECT ...'" # WRONG — postgres container is not on the services VM ssh -i ~/.ssh/id_ed25519_hiveiq bcosadmin@173.231.195.250 \ "docker exec hiveiq-postgres ..." ``` ### NEVER use backslashes in fleet task payloads All paths in `configPatch` (LOG_COLLECT_PATH, CONFIG_UPDATE, etc.) must use **forward slashes**. The agent normalizes separators on Windows. ```json { "path": "C:/hiveops-agent/logs/hiveops-agent.log" } // CORRECT { "path": "C:\\hiveops-agent\\logs\\hiveops-agent.log" } // WRONG ``` ### Generating a JWT without a real user account ```python import jwt, time secret = '' token = jwt.encode({ 'sub': '1', 'email': 'admin@bcos.cloud', 'role': 'MSP_ADMIN', 'iat': int(time.time()), 'exp': int(time.time()) + 3600 }, secret.encode('utf-8'), algorithm='HS256') print(token) ``` Do not base64-decode the secret — pass the raw string encoded as UTF-8.