# Gate a feature on a verified trust path

Ask from your user's own node and use the locally verified verdict in your application policy.


**Build today** — compose signed reachability queries with the SDK's local proof verifier.

Your application chooses which feature a trust path can unlock. TrustGraph answers whether a path meets the submitted policy. Start with [early access](/early-access), an allowlisted origin, a bound and approved graph device, and the issuer pins supplied through trusted application configuration.

## 1. Choose the question

Use the signed-in user's node as the source and an explicitly selected persona as the target. `minTrust` is a score from 0 to 100; `maxHops` is 1 to 3. Use `scope: 'identity'` for the current circuit. Node identifiers go in the query; the authorizing device identifier goes in the signed-read envelope.

## 2. Sign and verify

Pass an initialized SDK client, the user's local device key, their node and device identifiers, a target node, and independently pinned issuer JWKS to this function. It returns the verified reachability verdict and throws if verification fails. Complete the client and key setup in the [quickstart](/quickstart).

```ts
import type { TrustGraphClient } from '@rebellion-systems/trust-graph-sdk';
import { signBrowserGraphRead } from '@rebellion-systems/trust-graph-sdk/browser';
import { createZkVerifier, wotPathVerificationKey } from '@rebellion-systems/trust-graph-sdk/zk';

export async function checkConnection(input: {
  graph: TrustGraphClient;
  privateKey: CryptoKey;
  myGraphNodeId: string;
  myGraphKeyId: string;
  targetGraphNodeId: string;
  pinnedAttestationJwks: { keys: (JsonWebKey & { kid: string })[] };
}): Promise<boolean> {
  const query = {
    sourceGraphKeyId: input.myGraphNodeId,
    targetGraphKeyId: input.targetGraphNodeId,
    policy: { scope: 'identity' as const, minTrust: 50, maxHops: 3 },
  };
  const authorization = await signBrowserGraphRead(input.privateKey, {
    operation: 'query-reachable',
    graphKeyId: input.myGraphKeyId,
    graphNodeId: input.myGraphNodeId,
    query,
  });
  const response = await input.graph.queryReachability({ ...query, authorization });
  const verifier = createZkVerifier();
  try {
    const result = await verifier.verifyAttestation({
      verificationKey: wotPathVerificationKey(),
      attestation: response.attestation,
      jwks: input.pinnedAttestationJwks,
      question: {
        sourceGraphKeyId: query.sourceGraphKeyId,
        targetGraphKeyId: query.targetGraphKeyId,
        ...query.policy,
      },
    });
    if (!result.verified) throw new Error(`Answer rejected: ${result.code}`);
    return result.verdict.reachable;
  } finally {
    await verifier.destroy();
  }
}
```

Sign the exact submitted query with a fresh nonce on every attempt, including retries. Only use `result.verdict` after successful verification. A verification error is an unknown outcome; do not turn it into a negative path report.


**Never**
Never authorize a positive result from the response's unsigned convenience fields, a checksum or a server signature alone. Never replace your pinned issuer keys or circuit verification key with keys supplied by the answer's issuer.

## 3. Apply your application's authorization rules

Bind the check to the application's selected user, target and policy. Keep normal authentication, action permissions and freshness requirements alongside the graph check. A trust path is one input to that decision.


**Establishes**
A verified positive establishes a qualifying path in the committed graph at evaluation time without revealing its intermediate nodes and edges to the recipient.


**Does not establish**
A negative result is an operator-signed report, not a proof that no path exists. Proof validity does not establish that the committed graph is complete or latest, that the target is human, or that an action is safe. The service sees the graph and query; the proof does not hide them from the service.
