Auth: Source-device signed edge 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/edges/request HTTP/1.1
Host: trust-staging.rebellion.systems
Content-Type: application/json
{
"signerGraphKeyId": "<source-node-id>",
"subjectGraphKeyId": "<target-node-id>",
"signingGraphKeyId": "<source-device-id>",
"trust": 80,
"scope": "identity",
"createdAt": "<current-ISO-timestamp>",
"nonce": "<fresh-random-nonce>",
"signature": "<base64-edge-signature>"
}Response shape:
{
"edge": "<active-edge-object>",
"graphEpochRoot": "<graph-root>",
"poseidonEpochRoot": "<poseidon-root>",
"eventLogRoot": "<log-root>",
"eventLogSize": "<nonnegative-integer>"
}A declaration is a one-way score from one node to another, active immediately. Scores are integers from 0 to 100. New declarations use scope: "identity"; this is a protocol compatibility constant, not a relationship label.
Declare trust
POST /v1/edges/request accepts a signed edge certificate:
{
"signerGraphKeyId": "source-node-id",
"subjectGraphKeyId": "target-node-id",
"signingGraphKeyId": "source-device-id",
"trust": 80,
"scope": "identity",
"createdAt": "2026-11-04T12:00:00.000Z",
"nonce": "fresh-random-nonce",
"signature": "base64-edge-certificate-signature"
}expiresAt is optional. All timestamps must parse. Sign the certificate fields using the SDK's edge signing helper; signature and signingGraphKeyId are not certificate preimage fields. Include signingGraphKeyId when the signing device ID differs from the source node ID.
Success is 201 with {edge, graphEpochRoot, poseidonEpochRoot, eventLogRoot, eventLogSize}. A fresh declaration for the same direction atomically supersedes the previous one. Public edge projections omit the certificate signature and nonce.
Conditional replacement
POST /v1/edges/replace accepts the same body but requires the signed nonce:
replace-v1.<current-edge-id>.<random>The random suffix is 8–128 letters, digits or hyphens. The declaration applies only if the named edge is still current. This is the preferred route when a user edits a score they previously read.
| Code | Meaning |
|---|---|
400 EDGE_REPLACE_NONCE_REQUIRED | Replacement nonce missing |
400 EDGE_NONCE_INVALID | Replacement nonce has an invalid shape |
400 EDGE_REPLACE_ROUTE_REQUIRED | Conditional certificate sent to /request |
409 EDGE_SUPERSEDED | Another declaration is current; response includes currentEdgeId |
409 EDGE_NOT_CURRENT | No declaration currently stands |
A refused conditional certificate consumes its nonce; a wrong-route refusal does not. Refresh state and sign anew. If /replace returns 404 because it is unsupported, stop the conditional operation.
List incident edges
POST /v1/edges/list takes {graphKeyId, authorization}, with read operation list-edges. Here the query's graphKeyId is the own node ID, while authorization.graphKeyId is its signing device. The response is {edges, edgeTags}.
Incoming and outgoing edges are included. Outgoing scores are visible to the owner; incoming projections do not disclose the other source's private score. Private tags appear only on supported current outgoing edges. Optional handle and deactivation fields help display already-visible endpoints. GET /v1/edges always returns 401.
Revoke and legacy confirm
POST /v1/edges/:edgeId/revoke takes {action, signature}. Sign action revoke-edge with graphKeyId, edgeId, nonce and signedAt. Only a usable device of the source node can revoke its declaration. The response contains edge and the epoch commitment fields. Removing a superseded edge can return EDGE_SUPERSEDED; refresh before asking the user to remove the current declaration.
POST /v1/edges/:edgeId/confirm remains for legacy clients. It takes the corresponding signed confirm-edge action from the subject node's device. On active trust it is an authenticated no-op; it does not create a reciprocal declaration. Revoked trust cannot be confirmed.