RunbooksHub 401

Hub returns 401 Unauthorized

Applies to: core, hub

Symptom

Core logs show requests to the HubHubThe Go service that owns background processing, integrations, and the admin API. Sibling to Core. rejected:

POST http://hivecfm-hub-api:8080/v1/feedback-records 401 (Unauthorized)
{"error":"Missing Authorization header"}
{"error":"Invalid Authorization header format. Expected: Bearer <api-key>"}

A direct curl with no credentials reproduces it:

curl -i http://localhost:8090/v1/feedback-records
# HTTP/1.1 401 Unauthorized

Likely cause

The HubHubThe Go service that owns background processing, integrations, and the admin API. Sibling to Core. authenticates every /v1/* request with middleware.Auth(cfg.APIKey) — it expects:

Authorization: Bearer <api-key>

…where <api-key> matches the HubHubThe Go service that owns background processing, integrations, and the admin API. Sibling to Core.’s API_KEY env var exactly. A 401 means one of:

  1. Keys drifted. API_KEY in hivecfm-hub/.env does not equal HIVECFM_HUB_API_KEY in hivecfm-core/.env (or .env.local).
  2. No Authorization header. Core was pointed at the Hub but not given the key (empty env var).
  3. Wrong header shape. Something is sending the raw key instead of Bearer <key>.

Fix

Read both sides of the key

grep '^API_KEY=' /home/ubuntu/AG-DEV/hivecfm-hub/.env
grep '^HIVECFM_HUB_API_KEY=' /home/ubuntu/AG-DEV/hivecfm-core/.env
grep '^HIVECFM_HUB_API_KEY=' /home/ubuntu/AG-DEV/hivecfm-core/.env.local 2>/dev/null

The values must be identical strings. If .env.local exists, it wins for native dev.

Fix the mismatch

Pick one value and write it on both sides:

# In hivecfm-hub/.env
API_KEY=hivecfm-hub-secret-key
 
# In hivecfm-core/.env (and .env.local if present)
HIVECFM_HUB_API_KEY=hivecfm-hub-secret-key
HIVECFM_HUB_URL=http://hivecfm-hub-api:8080    # Docker
# or HIVECFM_HUB_URL=http://localhost:8090     # native dev

Hub-side env var is API_KEY; core-side env var is HIVECFM_HUB_API_KEY. The names are different on purpose — same secret, two config files.

Restart both services

# Docker
docker compose restart hivecfm-hub-api hivecfm-core
 
# Native dev
# Stop pnpm dev / `make run` and start them again

Smoke-test from the shell

curl -i -H "Authorization: Bearer $API_KEY" \
  http://localhost:8090/v1/feedback-records
# expect 200 (or 404 with a JSON body), not 401

Verify

  • Core logs no longer show 401 when calling the Hub.
  • curl -i http://localhost:8090/health returns 200 with no auth header (health is public).
  • curl -i -H "Authorization: Bearer <key>" http://localhost:8090/v1/webhooks returns 200 or the expected JSON payload.

Prevent

  • Treat the Hub key as one secret with two names — change it in hivecfm-hub/.env and hivecfm-core/.env together, never one at a time.
  • The Hub will refuse to start with an empty API_KEY; if Hub is healthy, the key is set — the bug is on the caller side.
  • When rotating keys in shared environments, restart both services in the same window to avoid a window where one side still holds the old value.