d87c3490c5
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
205 lines
7.5 KiB
Markdown
205 lines
7.5 KiB
Markdown
# 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=<key>
|
||
```
|
||
|
||
### 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":"<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/<service>/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 = '<JWT_SECRET value from .env>'
|
||
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.
|