Auth: Own-device signed read, operation issue-device-certificate.
Request and response
Angle-bracket values describe placeholders, including nested objects and numeric types; these are wire shapes, not captured service data. Replace them before sending.
POST /v1/graph-keys/certificate HTTP/1.1
Host: trust-staging.rebellion.systems
Content-Type: application/json
{
"graphKeyId": "<own-device-id>",
"graphNodeId": "<own-node-id>",
"authorization": {
"graphKeyId": "<device-id>",
"nonce": "<fresh-random-nonce>",
"signedAt": "<current-ISO-timestamp>",
"signature": "<base64-DER-signature>"
}
}Response shape:
{
"certificate": "<issuer-signed-device-certificate>"
}A membership certificate links a device public key to a stable node through an issuer signature. It can travel with signed content so recipients can verify the content locally and separately check current device status.
Issue your own certificate
POST /v1/graph-keys/certificate
Send {graphKeyId, graphNodeId, authorization}. Sign read operation issue-device-certificate over the exact two-field query. The signing device must be the requested key and belong to the requested node. Graph-purpose devices on chained nodes require approval; content-signing keys can request only their own certificate.
The response is {certificate}:
export type DeviceMembershipCertificate = {
v: 'trust-graph-device-certificate-v1';
issuer: string;
certificateId: string;
graphKeyId: string;
graphNodeId: string;
publicKeyJwk: { kty: 'EC'; crv: 'P-256'; x: string; y: string };
issuedAt: string;
expiresAt: string;
attestationKeyId: string;
signature: string;
};The issuer is the exact API origin pinned by your application. The Ed25519 signature covers canonical JSON of the other fields. Certificate lifetime is capped at seven days, the device's expiry and, for content-signing keys, the remaining signing window. Issuance requires positive remaining lifetime; exact expiry returns 403 GRAPH_KEY_EXPIRED after proof of possession.
Check lifecycle status
POST /v1/graph-keys/certificate/status accepts {certificate, nonce}. Use a fresh nonce of 16–256 characters. The complete valid issuer-signed certificate authorizes this check; no graph-device read authorization is needed.
The response is {status}. The signed status object contains v: "trust-graph-device-status-v1", certificateDigest, nonce, evaluatedAt, status, revokedAt, attestationKeyId and signature. The digest covers canonical JSON of the complete certificate, including its signature.
| Status | Meaning |
|---|---|
valid | Certificate and device pass current lifecycle checks |
revoked | Device has been revoked; revokedAt supplies its time |
expired | Device, certificate or content-signing window has expired |
unavailable | Membership cannot be established |
Verify both issuer signatures, pinned origin and key, certificate times, certificate digest, nonce and status freshness. Renewal does not extend an existing certificate. Invalid issuer signatures are refused with 401. Both issuance and status use query rate limits.