# Reachability

Ask whether directed trust reaches a target under your policy.


**Auth:** Own-device signed read, operation `query-reachable`.
## 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.

```http
POST /v1/query/reachable HTTP/1.1
Host: trust-staging.rebellion.systems
Content-Type: application/json

{
  "sourceGraphKeyId": "<own-node-id>",
  "targetGraphKeyId": "<target-node-id>",
  "policy": {
    "maxHops": 3,
    "minTrust": 50,
    "scope": "identity"
  },
  "authorization": {
    "graphKeyId": "<device-id>",
    "nonce": "<fresh-random-nonce>",
    "signedAt": "<current-ISO-timestamp>",
    "signature": "<base64-DER-signature>"
  }
}
```

Response shape:

```json role="response"
{
  "reachable": "<boolean>",
  "pathLength": "<integer-or-null>",
  "effectivePolicy": "<effective-policy-object>",
  "attestation": "<signed-attestation-object>",
  "proof": "<legacy-proof-or-null>",
  "integrity": {
    "authoritative": "attestation",
    "unsignedProjections": [
      "reachable",
      "pathLength",
      "effectivePolicy",
      "proof"
    ]
  }
}
```




`POST /v1/query/reachable` asks from the authenticated device's own node. Sign read operation `query-reachable` over the exact query body before adding `authorization`.

| Field | Meaning |
| --- | --- |
| `sourceGraphKeyId` | Your stable node ID |
| `targetGraphKeyId` | Target stable node ID |
| `policy.maxHops` | Integer 1–3, default 3 |
| `policy.minTrust` | Integer 0–100, default 50 |
| `policy.scope` | `identity`, the default and supported new-query scope |
| `policy.currentTime` | Optional future horizon, parseable timestamp |
| `authorization` | Fresh own-device signed read |

Include all intended policy values explicitly. Out-of-range values return 400 rather than being clamped. A horizon can only narrow the answer: edges must qualify now and at that future time. A past horizon is rejected. The server chooses evaluation time and the current graph commitment.

```typescript
import {
  createTrustGraphSdk,
  signBrowserGraphRead,
} from '@rebellion-systems/trust-graph-sdk/browser';

export async function requestReachability(
  privateKey: CryptoKey,
  deviceId: string,
  ownNodeId: string,
  targetNodeId: string,
) {
  const graph = createTrustGraphSdk({
    serverUrl: 'https://trust-staging.rebellion.systems',
  });
  const query = {
    sourceGraphKeyId: ownNodeId,
    targetGraphKeyId: targetNodeId,
    policy: { maxHops: 3, minTrust: 50, scope: 'identity' as const },
  };
  const authorization = await signBrowserGraphRead(privateKey, {
    operation: 'query-reachable',
    graphKeyId: deviceId,
    graphNodeId: ownNodeId,
    query,
  });
  return graph.queryReachability({ ...query, authorization });
}
```

This function requests evidence; it does not verify that evidence. Verify `response.attestation` locally with the SDK verifier and your pinned keys before acting on it.

## Response

The response includes `reachable`, `pathLength`, `effectivePolicy`, the legacy `proof`, the signed `attestation`, and `integrity`. Optional `sourceHandle` and `targetHandle` are display metadata.

`integrity.authoritative` is `attestation`. Its `publicInputs` carry the signed answer, endpoints, policy, evaluation time, horizon and commitment fields. Top-level convenience fields are unsigned projections.


**Establishes**
A locally verified positive v2 attestation establishes a qualifying non-empty directed path in the committed graph. The path can have at most three hops. Intermediate nodes and edges are not disclosed by the proof.


**Does not establish**
A negative answer is a signed server report, not a zero-knowledge proof of absence. The service sees the graph and query. A valid proof does not establish that the published graph is complete or the latest state. A path returning to its starting node can satisfy a query from a node to itself.

## Errors

Malformed policy returns 400. An invalid or missing read signature returns 401; a device from another source node is refused. Lifecycle and approval failures use the common [authorization errors](/api). Unknown or deactivated endpoints are unavailable. Proof-generation failure returns 503 instead of a signature-only positive.

See [attestations](/api/attestations) for local verification and the server verification endpoint.
