Use Relay v2
Prepare, register, subscribe, and launch through the official Relay v2 session API.
Use Relay v2
Relay v2 is a short-lived result bridge for dApps that do not want to receive a direct wallet callback. The dApp creates a session, registers it with the official relay, subscribes with a read URL, and gives the wallet only the callback URL. This path does not require a callback page or public callback route in the dApp. For the larger mental model, see Understand Glyph Connect and Relay v2.
The lifecycle
1. Create the request
Build the request before preparing delivery. The nonce and expiry become part of the request binding.
const request = createSignMessageRequest({
type: "sign_message",
dapp: { origin: "https://example.app" },
message: "Sign in to Example",
});2. Prepare and register the session
import { prepareRelaySession } from "@glyph-oss/connect";
const session = await prepareRelaySession();prepareRelaySession() generates distinct opaque session, callback, and read values, then sends the callback and read values to POST /v2/register/:session. A successful registration returns a prepared session with:
callbackUrlfor the wallet's POST callbackstreamUrlfor the dApp's SSE subscriptionresultUrlfor dApp polling- the session values used to construct those URLs
Registration must happen before launching the wallet. The official relay URL is fixed to https://relay.glyphq.org.
3. Subscribe before launch
Create the envelope with session.callbackUrl, then subscribe with the prepared session:
import { k12, verify } from "@qubic.org/crypto";
import {
createEnvelope,
subscribeViaRelayV2,
} from "@glyph-oss/connect";
const envelope = createEnvelope(request, {
callback: session.callbackUrl,
});
const resultPromise = subscribeViaRelayV2(request, session, {
verification: {
requireSigned: true,
expectedRequestHash: envelope.request_hash,
expectedNetwork: envelope.network,
expectedDappOrigin: request.dapp.origin,
expectedExp: request.exp ?? null,
expectedCallbackUrl: session.callbackUrl,
verifySignature: ({ algorithm, payload, signature, publicKey }) => {
if (algorithm !== "qubic-schnorrq-sha256") return false;
return verify(k12(payload, 32), signature, publicKey);
},
},
});The subscription uses session.streamUrl, which is authorized for reading. The wallet never receives that URL.
@qubic.org/crypto verifies the signed callback in the dApp. Glyph Wallet signs the callback after approval, and the dApp must not become a signer by holding private key material in browser code.
4. Launch and await
import { launchGlyphRequest } from "@glyph-oss/connect";
launchGlyphRequest(envelope);
const result = await resultPromise;The relay emits a result SSE event when the wallet posts a JSON callback. The SDK parses and verifies it before resolving. It rejects on an invalid HTTP response, missing body, stream timeout, timeout event, close event without a result, or callback verification error.
Keep authorities separate
- Put
callbackUrlonly in the envelope sent to the wallet. - Keep
streamUrlandresultUrlinside the dApp process. - Do not log or send session URLs to analytics or third parties.
- Do not reuse a callback URL as a read URL.
- Do not call
subscribeViaRelayV2()until registration has succeeded.
The relay stores the first accepted result for up to ten minutes. The SSE wait is five minutes and a session supports at most five listeners. A second callback for the same session is rejected. Treat each session as short-lived and single-use for delivery, and create a fresh session for the next request.
Polling instead of SSE
The SDK exposes resultUrl for a read-only GET. Before a result arrives, Relay returns HTTP 404 with { "status": "pending" }. After delivery, it returns HTTP 200 with { "status": "ok", "result": ... }. Polling does not remove the need to verify a signed callback envelope.
