Hera XOVEREIGN

Reference

Hera API

One REST interface across all seven tiers. The tier a request runs at is a property of the key that authorises it, not a parameter the caller chooses — so a key issued for Tier 2 cannot ask for Tier 6 protection, and an application does not need to know which tier it is running under to be correct.

Base URL $QX_BASE_URL Issued at provisioning. Every example on this page reads it from the environment rather than naming a host.
Transport HTTPS only Requests over plain HTTP are refused, not redirected. A redirect would have already sent the credential.
Payload format JSON UTF-8. Binary is carried base64-encoded in named fields, never as a raw body.

Authentication

Every request carries an issued API key as a bearer credential. Keys are minted in the operator console, are shown once at mint time and are never retrievable afterwards — the plane stores a digest, not the key.

HTTP
POST /v1/protect HTTP/1.1
Host: your-plane.example
Authorization: Bearer qx_live_XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX
Content-Type: application/json
Idempotency-Key: 6f1c2b9a-4d3e-4f8a-9c11-7b5e2a0d8c44

Key handling

  • A key is a credential. Keep it in a secret store, never in source, never in a repository, never in a client-side bundle.
  • Scope each key to the operations one workload actually needs. A key that can only protect cannot open.
  • Revocation takes effect on the next request. There is no propagation window to wait out.
  • Rotate keys on a schedule you set, independently of the automatic rotation of the containment key material.

Idempotency

Supply Idempotency-Key on any state-changing call. A repeat of the same key within 24 hours returns the original response rather than performing the operation again. This matters most on rotate and heal, where a retry after a network timeout would otherwise do real work twice.

Read calls ignore the header.

Endpoints

POST

/v1/protect

Places a record inside the containment of the tier the key is issued for. Returns a protected payload and a separate recovery handle.

Request
data — base64 of the bytes to protect. domain — a caller-chosen label that separates one class of record from another. Optional aad — additional authenticated data, bound to the record but not encrypted.
Returns
payload — safe to store and transmit; contains no key material. recovery — required to open the record later. barriers, tier, protectedAt.
Guarantee
A payload altered by one bit fails to open. It does not open incorrectly.
Rate cost
1 unit per call, plus 1 unit per 256 KiB beyond the first.

Store payload and recovery apart, under different access control. Held in one place they are, together, the plaintext. The API returns them in one response because it has to; your storage layer has no such excuse.

POST

/v1/open

Opens a protected record. This is the decrypt operation.

Request
payload and recovery as returned by protect. Optional aad, which must match what was supplied at protect time.
Returns
data — base64 of the original bytes. verified — the checks that passed on the way out.
Guarantee
Every barrier is verified before any plaintext is produced. A failure at any barrier returns an error, never a partial result.
Errors
422 containment_failed when a barrier does not verify. The response names which class of barrier failed and nothing more — a detailed failure position is an oracle.
Rate cost
1 unit per call, plus 1 unit per 256 KiB beyond the first.

POST

/v1/sign

Signs a message under the key's signing identity. Available from Tier 3. Below Tier 3 the call returns 409 tier_insufficient rather than silently signing with fewer families.

Request
message — base64. identity — the signing identity to use, from the console.
Returns
signatures — one entry per family, each opaque. identityId — what a verifier pins. families — how many signatures a verifier must check.
Guarantee
Two independent families sign every message, and verification requires both. Public key material is never returned in a signature response — a verifier resolves it from its own trust store, because a public key travelling beside its own signature is a public key an attacker can replace.
Rate cost
4 units per call. Signature generation is the most expensive operation in the interface.

POST

/v1/verify

Verifies a signed message. Available from Tier 3.

Request
message, signatures, identityId.
Returns
valid — true only when every family verified. checks — one entry per family with its own verdict. deferred — checks that genuinely could not be performed, named rather than assumed to have passed.
Guarantee
valid: true means every applicable check ran and passed. It is never returned for a check that was skipped: anything unperformed appears under deferred, where a caller can see it.
Rate cost
2 units per call.

POST

/v1/keys/rotate

Rotates containment key material ahead of the tier's automatic cadence. Rotation is normally automatic; this endpoint exists for the case where it must happen now — a suspected exposure, a departing administrator, an audit requirement.

Request
scope — domain or all. reason — recorded in the audit log, required, free text.
Returns
rotatedAt, nextAutomaticAt, affectedDomains, resealed.
Guarantee
Rotation and re-sealing are one operation. The call does not return until every affected record is sealed under the new material, so there is no window in which the estate holds keys that cannot open it.
Rate cost
10 units, and one concurrent rotation per domain.

GET

/v1/spheres/health

Reports the live state of every containment sphere. Each entry is the result of an actual seal verification performed for this request — not a cached status.

Query
domain — optional, narrows the report.
Returns
spheres — index, verdict, when it was last sealed, when it was last healed. barriers — total for the tier. healthy — true only if every sphere verified. checkedAt.
Guarantee
A sphere reported healthy had its seal recomputed and matched during this request. A sphere reported punctured did not.
Rate cost
1 unit. Does not count against the burst budget.

GET

/v1/spheres/specification

The properties of a containment sphere, and the two published formulas the depth and the regeneration budget come from. Static for a given engine version, so it is safe to cache against the engineVersion it returns.

Returns
depthDigits — entropy depth of one sphere, in decimal digits. depthBytes — the same depth as stored. sectors — meridian sectors; depth is identical along every one of them. addressableBytes — sectors multiplied by depth. regenerationBudgetNs — the flat budget every sphere is held to, at every tier. barriers — barriers in the full architecture. formulas — both published formulas as strings, with the value each evaluates to. engineVersion.
On the regeneration figure
regenerationBudgetNs is enforced, not derived: the engine times every regeneration against a monotonic clock and counts the misses. The published speed formula is returned alongside it under formulas so the derivation can be checked, but it is level-dependent and the engine's behaviour is not computed from it.
On the depth figure
depthDigits is the same at every tier. The entropy formula is dominated by its φc term, so the tier's Fibonacci index moves the result by less than one digit in 23.5 million.
Rate cost
0 units. Specification reads are not metered.

GET

/v1/spheres/regeneration

What the engine has actually measured, as distinct from what it is specified to do. This is the endpoint to alert on: it reports budget misses rather than hiding them behind an average.

Returns
regenerations — lifetime count. budgetViolations — regenerations that ran over budget. compliance — the fraction that met it, 0 to 1. meanNs, worstNs. absorbedDigits — entropy taken in from suffocated payloads. trapped — payloads currently degrading. measuredAt.
Guarantee
compliance and meanNs describe the same population of events. A mean inside the budget alongside a low compliance figure would be arithmetically impossible, and this endpoint will not emit that pair.
Why worst, and not a percentile
An attacker is not the average request. worstNs is the single slowest regeneration observed, unsmoothed.
Rate cost
1 unit. Does not count against the burst budget.

POST

/v1/spheres/{index}/heal

Repairs a punctured sphere with entirely fresh key material. Healing is automatic on detection; this endpoint forces it.

Returns
healed, resealedAbove — the spheres above the repaired one that were re-sealed as a consequence, healedAt.
Guarantee
Repairing a sphere re-seals every sphere above it, because their inputs moved. A heal that only silenced the alarm would leave a chain that verifies and does not open.
Rate cost
8 units.

GET

/v1/posture · /v1/compliance

Posture returns the current score and the facts it was computed from. Compliance returns the standards mapping for the licensed tier, including rows that are partial, each with the specific reason stated.

Returns (posture)
score 0–100, factors — each with its own contribution and evidence, computedAt.
Returns (compliance)
rows — framework, publishing body, publication date, verdict, note. partial — the count of rows that are not fully met.
Guarantee
A row is never reported as met when it is not. Where this product does not meet a requirement, the report says so and names what is missing.
Rate cost
1 unit each.

Examples

The same protect-then-open round trip in three environments. No SDK is required — the interface is HTTP and JSON, and these use only what each language ships with.

curl · protect, then open
# The plane's address and your key both come from the environment.
# Never inline a credential into a command that lands in shell history.
: "${QX_BASE_URL:?set QX_BASE_URL}"
: "${QX_API_KEY:?set QX_API_KEY}"

PLAINTEXT=$(printf 'account 4471 closing balance 918204' | base64)

# 1. Protect. The response carries two halves: keep them apart afterwards.
RESPONSE=$(curl --fail-with-body --silent --show-error \
  --request POST "$QX_BASE_URL/v1/protect" \
  --header "Authorization: Bearer $QX_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data "$(printf '{"data":"%s","domain":"ledger.balances"}' "$PLAINTEXT")")

echo "$RESPONSE" | python3 -c 'import json,sys; d=json.load(sys.stdin); \
print("barriers:", d["barriers"], "tier:", d["tier"])'

# 2. Open it again. Both halves are required; neither alone is enough.
curl --fail-with-body --silent --show-error \
  --request POST "$QX_BASE_URL/v1/open" \
  --header "Authorization: Bearer $QX_API_KEY" \
  --header "Content-Type: application/json" \
  --data "$RESPONSE" \
  | python3 -c 'import json,sys,base64; d=json.load(sys.stdin); \
print(base64.b64decode(d["data"]).decode())'

# 3. Check containment. Every sphere below was verified for this request.
curl --fail-with-body --silent --show-error \
  --request GET "$QX_BASE_URL/v1/spheres/health?domain=ledger.balances" \
  --header "Authorization: Bearer $QX_API_KEY"

Rate limits

Limits are per key, per minute, in units rather than requests — because a signature costs four times what a protect costs and a rate limit that counted them the same would be wrong in both directions.

Sustained is the steady-state budget. Burst is the depth of the token bucket, refilled at the sustained rate. Exceeding it returns 429 with a Retry-After header, never a silent drop.
Tier Sustained, units/min Burst Support response
1 · AMBER1,200240Business hours, 2 business day response
2 · SAPPHIRE2,400480Business hours, 1 business day response
3 · EMERALD3,60072024×5, 4 hour response
4 · RUBY6,0001,20024×7, 1 hour response
5 · DIAMOND12,0002,40024×7, 30 minute response, named engineer
6 · OBSIDIAN30,0006,00024×7, 15 minute response, named engineer, sovereign escrow briefing
7 · TORTOISEDedicated planeDedicated plane24×7, 15 minute response, dedicated plane, on-site commissioning

Unit cost per operation

protect
1 unit, +1 per 256 KiB beyond the first
open
1 unit, +1 per 256 KiB beyond the first
verify
2 units
sign
4 units
spheres/heal
8 units
keys/rotate
10 units, one concurrent rotation per domain
spheres/health
1 unit, exempt from burst
posture, compliance
1 unit each

Headers on every response

X-RateLimit-Limit
Sustained units per minute for this key
X-RateLimit-Remaining
Units left in the current window
X-RateLimit-Reset
Seconds until the window refills
X-Quantum-Tier
The tier this key ran at
X-Barriers
Barriers maintained on this operation
Retry-After
On 429 only. Seconds. Honour it.

A higher tier grants a larger budget and also costs more time per operation. The entitlement rises faster than the cost, but not without limit — size against measured throughput during evaluation, not against this table alone.

Errors

Every error is JSON with the same shape. Status codes carry their ordinary HTTP meaning and are not reused for application signalling.

JSON
{
  "type": "containment_failed",
  "title": "A barrier did not verify",
  "status": 422,
  "detail": "Sphere seal verification failed. The record was not opened.",
  "instance": "req_8f21c07a4b",
  "retryable": false
}
StatusTypeMeaning
400malformed_requestThe body did not parse, or a required field is missing.
401unauthenticatedNo credential, or one that is not recognised. Returned as JSON with status 401 — never as a redirect to a sign-in page, which a programmatic caller would read as a success.
403scope_deniedThe key is valid but is not scoped for this operation.
404not_foundNo such domain, identity or sphere index.
409tier_insufficientThe operation requires a higher tier than this key holds. The call is refused rather than performed with fewer protections.
409rotation_in_progressA rotation is already running for this domain.
413payload_too_largeAbove the per-record ceiling. Chunk and protect each part.
422containment_failedA barrier did not verify. Nothing was returned. The failing barrier class is named; its position is not, because a position is an oracle.
423sovereign_requiredTiers 6 and 7. The action needs sovereign authorisation, which an API key cannot supply.
429rate_limitedBudget exhausted. Retry-After is present and accurate.
503healing_in_progressContainment is being repaired. Retryable; the header says when.

Two things this API will not do

It will not return a partial result. If any barrier fails, the response is an error. There is no mode in which a record comes back half-verified with a warning attached, because a caller that receives bytes will use them.

It will not report a check it did not run. Anything that could not be performed appears in deferred with the reason. A verification interface that reports success for work it skipped is worse than no verification, because it is trusted.

Versioning

The version is in the path. Within a version, fields are added but never removed and never change meaning; a client that ignores unknown fields will not break. Payload formats carry their own version, and a payload protected under an earlier one continues to open — a record whose containment outlived its reader is a record that is gone.

Deprecation of a path is announced through the console and by a Deprecation header on every response from it, no less than twelve months before removal.