Skip to main content

Authentication and Identity

Every request to Genesis X-1 is scoped to a specific actor. There is no anonymous access to authenticated surfaces. There is no shared credential. Every operation is attributed to the actor whose credential authorized it.

Authentication is not a gate. It is the identity anchor for every event, every delegation, and every access decision recorded in the protocol.

Identity Paths

1. API key authentication

Use Authorization: Bearer <iaex_sk_...> for developer and system access.

This is the primary production authentication model.

POST /developer/register issues:

  • one sandbox actor and API key
  • one production actor and API key

2. Explicit actor identity compatibility path

If a request does not carry an API key, the public API accepts:

  • X-ACTOR-ID
  • X-ACTOR-TYPE

This path exists for explicit actor-native operations where the request must act as a specific actor.

Request Identity Resolution

Identity resolution follows a strict order:

  1. If Authorization: Bearer contains a valid IAEX API key, that actor is the caller.
  2. Otherwise the server reads X-ACTOR-ID and X-ACTOR-TYPE.

If both are present, the bearer key takes precedence.

Required Headers

API key requests

  • Authorization: Bearer <iaex_sk_...>
  • Content-Type: application/json for JSON request bodies

Optional:

  • X-Actor-Type to override the default service actor type for API-key-authenticated requests
  • X-Request-Id for client correlation
  • Idempotency-Key for POST, PUT, and PATCH
  • X-Delivery-Chain when forwarding a caller lineage

Explicit actor requests

  • X-ACTOR-ID: <actor_uuid>
  • X-ACTOR-TYPE: human|service|external|device

Actor Signature Model

Actor signatures authorize caller-signed event writes.

The public header is:

X-Actor-Sig: <base64-ed25519-signature>

Use X-Actor-Sig on caller-defined event append requests such as POST /traceledger/master/{uuid}/events.

Do not assume every mutating route requires a caller-supplied signature header. Some public routes generate their own platform event payloads as part of the route contract.

The actor signature proves:

  • which actor authorized the event append
  • the exact event type
  • the exact ledger target
  • the exact canonical payload submitted by the actor before delivery-chain injection

The actor signature does not prove:

  • truth of the business content
  • downstream business approval by another party
  • chain continuity on its own

What the Actor Signature Covers

The signature input is:

SHA-256(
event_type + "\x00" + ledger_id + "\x00" + canonical_json(payload_without_delivery_chain)
)

The digest is then signed with Ed25519.

For POST /traceledger/master/{uuid}/events, ledger_id in the signature input is the parent ledger id of that master, not the master uuid.

Canonical JSON means deterministic JSON with recursively sorted object keys.

Minimal Signature Example

The example below signs an event request using a base64-encoded 32-byte Ed25519 seed in ED25519_SEED_B64.

export EVENT_TYPE=AI_RESPONSE
export LEDGER_ID="$LEDGER_ID"
export PAYLOAD_JSON='{"model":"genesis-x1-audit","summary":"Inspection complete","traceledger_master_uuid":"'"$MASTER_ID"'"}'
import { createHash, createPrivateKey, sign } from "node:crypto";

const canonicalize = (value) => {
if (Array.isArray(value)) {
return `[${value.map(canonicalize).join(",")}]`;
}
if (value && typeof value === "object") {
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonicalize(value[k])}`).join(",")}}`;
}
return JSON.stringify(value);
};

const pkcs8Prefix = Buffer.from("302e020100300506032b657004220420", "hex");
const seed = Buffer.from(process.env.ED25519_SEED_B64, "base64");
const privateKey = createPrivateKey({
key: Buffer.concat([pkcs8Prefix, seed]),
format: "der",
type: "pkcs8"
});

const payload = canonicalize(JSON.parse(process.env.PAYLOAD_JSON));
const message = Buffer.concat([
Buffer.from(process.env.EVENT_TYPE),
Buffer.from([0]),
Buffer.from(process.env.LEDGER_ID),
Buffer.from([0]),
Buffer.from(payload)
]);

const digest = createHash("sha256").update(message).digest();
const signature = sign(null, digest, privateKey);
process.stdout.write(signature.toString("base64"));

What Developers Must Store

Store:

  • API keys
  • actor private signing keys
  • webhook secrets returned at subscription creation
  • actor ids and organization or entity ids needed by your integration

Keep secret:

  • API keys
  • actor private signing keys
  • webhook secrets

Do not send private keys to IAEX.

Public Key Enrollment

Actor public keys are enrolled through the actor key endpoints.

Launch algorithm support:

  • Ed25519

The enrolled value is the raw 32-byte Ed25519 public key in base64.

Auth Failure Behavior

Authentication failures return a JSON error envelope with 401 or 404 depending on the failure mode.

Examples:

  • invalid or revoked API key: 401
  • missing identity headers when no API key is present: 401
  • invalid actor type: 401
  • non-public authority identity: 404

Minimal API Key Example

curl -sS "$BASE_URL/developer/me" \
-H "Authorization: Bearer $API_KEY"

Example response:

{
"actor_id": "2f7a5f0f-8cc1-4f20-95b8-a2f5488d6132",
"iaex_id": "iaex:actor:2f7a5f0f-8cc1-4f20-95b8-a2f5488d6132",
"role": "PARTICIPANT",
"environment": "production",
"created_at": "2026-04-21T06:20:00Z",
"keys": [
{
"key_id": "df7d0f4d-6626-4dc9-b56f-f847b2d5096b",
"key_prefix": "iaex_sk_live_in_",
"environment": "production",
"name": "default",
"created_at": "2026-04-21T06:20:00Z"
}
]
}