Gateway public-key registration
During bootstrap, Edge License Gateway generates an ES256 key pair locally and keeps the private key on the Host. It sends only the public JWK to Cloud through outbound HTTPS:
POST /api/v1/edge-gateways/{gatewayInstanceId}/signing-keys
The one-time enrollment token authorizes only this exact Tenant and Gateway binding. It is not a Product identifier, license credential, or reusable API credential. One Gateway may later enforce rights for multiple Products; Product authority comes from verified runtime snapshots, never from this request.
Request
Use Content-Type: application/json. Do not send Idempotency-Key: the
enrollment token is consumed once and an exact replay after success returns
409.
{
"tenantId": "019c4400-0000-7000-8000-000000000001",
"enrollmentToken": "<one-time-enrollment-token>",
"publicSigningJwk": {
"kty": "EC",
"crv": "P-256",
"kid": "gateway-key-2026-01",
"use": "sig",
"alg": "ES256",
"x": "<base64url-encoded-32-byte-x-coordinate>",
"y": "<base64url-encoded-32-byte-y-coordinate>"
}
}
The body contains exactly the three shown top-level fields and is limited to 4096 UTF-8 bytes. The JWK contains exactly the seven shown public fields. Unknown, duplicate, nested, malformed or non-canonical values are rejected. Never add a private scalar or PEM material. Never log the enrollment token.
Success
201 Created returns only safe public-key receipt data:
{
"signingKeyId": "019c4400-0000-7000-8000-000000000010",
"gatewayInstanceId": "019c4400-0000-7000-8000-000000000009",
"keyId": "gateway-key-2026-01",
"publicKeyThumbprint": "<rfc-7638-sha256-base64url>",
"registeredAtUtc": "2026-08-07T16:30:00Z",
"supersededKeyId": null
}
All responses include Cache-Control: no-store and Pragma: no-cache.
Registration does not activate the Gateway; Cloud trust-document issuance is a
separate step.
Errors and retry
Errors use the standard code, message, correlationId envelope and never
echo Tenant, Gateway, token or key material.
| HTTP | Code | Meaning |
|---|---|---|
400 | validation.failed | Body or public JWK is invalid, including forbidden private material |
401 | relay.enrollment_invalid | Token does not match the exact Tenant/Gateway enrollment |
403 | relay.forbidden | Enrollment expired or Gateway state forbids registration |
409 | relay.conflict | Token was consumed, key conflicts, or a concurrent registration won |
413 | validation.failed | Body exceeds 4096 bytes |
415 | validation.failed | Content type is not JSON |
503 | relay.unavailable | Stored registration state is temporarily unavailable |
Validation failures happen before token consumption, so the corrected request
may use the same token. After a timeout or 409, do not retry blindly: the
first request may have committed. Obtain a new enrollment through the operator
workflow. Repeated 503 with a correlation ID requires support investigation.
Security boundary
- Generate and protect the private key locally; Cloud accepts public material only.
- Use production TLS and outbound Host-to-Cloud connectivity. Cloud never opens an inbound connection to the Host and the Host never connects to Cloud RabbitMQ or a Cloud database.
- Do not use request configuration, Product codes or entitlements as identity.
- The complete Relay engine and Host internals remain proprietary; this page documents only the supported bootstrap wire contract.
See also Local Relay and Security overview.