Internal APIs -- Overview & HMAC Authentication
Internal APIs enable service-to-service communication within the Sphere platform. They use HMAC-SHA256 signatures for authentication instead of JWT tokens. These endpoints are not exposed through the Traefik gateway and should never be called from client applications.
Internal APIs are for service-to-service communication only. They are not accessible through the public gateway. All client-facing API requests must use JWT Bearer tokens as described in the Authentication guide.
HMAC-SHA256 authentication
Every internal API request must include an X-Sphere-Signature header containing a timestamped HMAC-SHA256 signature. The receiving service verifies this signature against its configured shared secret before processing the request.
Signature format
The signature header follows this format:
X-Sphere-Signature: t=<timestamp>,v1=<hmac>
- Name
t- Type
- integer
- Description
Unix timestamp (seconds since epoch) when the request was signed. Used for replay protection.
- Name
v1- Type
- string
- Description
HMAC-SHA256 hex digest computed over the signing payload.
Signing payload
The HMAC is computed over a concatenation of four components, joined by dots:
timestamp.method.path.body
- Name
timestamp- Type
- string
- Description
The same Unix timestamp used in the
t=parameter.
- Name
method- Type
- string
- Description
The HTTP method in uppercase (e.g.,
POST,GET,PATCH).
- Name
path- Type
- string
- Description
The request path without query string (e.g.,
/api/internal/orchestration/provision/tenant).
- Name
body- Type
- string
- Description
The raw request body as a string. For requests with no body (e.g., GET), use an empty string.
Signing example
Here is how to compute an HMAC signature for an internal API request. Each service stores its shared secret in its .env file. The sending and receiving services must share the same secret.
cURL with HMAC
# Compute the signature
TIMESTAMP=$(date +%s)
METHOD="POST"
PATH="/api/internal/orchestration/provision/tenant"
BODY='{"tenant_id":"9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d","tenant_short_id":"acme","name":"Acme Corp"}'
SECRET="your-shared-hmac-secret"
PAYLOAD="${TIMESTAMP}.${METHOD}.${PATH}.${BODY}"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)
curl -X POST http://core:7002/api/internal/orchestration/provision/tenant \
-H "Content-Type: application/json" \
-H "X-Sphere-Signature: t=${TIMESTAMP},v1=${SIGNATURE}" \
-d "$BODY"
JavaScript
import crypto from 'crypto'
const timestamp = Math.floor(Date.now() / 1000)
const method = 'POST'
const path = '/api/internal/orchestration/provision/tenant'
const body = JSON.stringify({
tenant_id: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d',
tenant_short_id: 'acme',
name: 'Acme Corp',
})
const secret = 'your-shared-hmac-secret'
const payload = `${timestamp}.${method}.${path}.${body}`
const signature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex')
const response = await fetch(
'http://core:7002/api/internal/orchestration/provision/tenant',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sphere-Signature': `t=${timestamp},v1=${signature}`,
},
body,
},
)
Replay protection
The receiving service enforces a 5-minute window on the timestamp. If the t= value is more than 5 minutes in the past or in the future relative to the server clock, the request is rejected with a 401 Unauthorized response. This prevents replay attacks where a captured request is re-sent at a later time.
Shared secret configuration
Each service stores the HMAC shared secret in its .env file. The exact variable name depends on the service pair. The reset-world.sh script propagates these secrets automatically during setup.
.env configuration
# In sphere-core .env
SPHERE_INTERNAL_HMAC_SECRET=your-shared-hmac-secret
# In sphere-auth .env
SPHERE_INTERNAL_HMAC_SECRET=your-shared-hmac-secret
All services in a Sphere instance share the same HMAC secret. This is generated once during initial setup by generate-sphere-secrets.sh and propagated to all services by reset-world.sh.
Architecture
Internal APIs follow a direct service-to-service communication model:
- Not exposed through Traefik -- internal endpoints use direct URLs (e.g.,
http://core:7002) rather than the gateway - Same HMAC secret -- all services in a sphere instance share a single HMAC secret
- No JWT required -- internal requests are authenticated by signature, not by user tokens
- Docker DNS -- in containerized deployments, services resolve each other by Docker DNS names (e.g.,
auth:7001,core:7002)
Internal API categories
The following categories of internal APIs are available:
- Name
Orchestration- Type
- /internal/orchestration
- Description
Tenant and user lifecycle management. Provision and deprovision tenants and users across all engines.
- Name
User Sync- Type
- /internal/user-sync
- Description
Synchronize user profile data from sphere-auth to sphere-core when profiles are updated.
- Name
Engine Registration- Type
- /internal/engine-registration
- Description
Register engines with sphere-core, project messages, and receive events from engines.