Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Glyph Relay

Bounded SSE result bridge for Glyph Wallet dApp integration, deployed on Cloudflare Workers with Durable Objects.

dApp ──── glyph:// deep link ──────→ wallet (Tauri)
dApp ← SSE /v2/stream/:session/:readCap ─ relay ← POST /v2/callback/:session/:callbackCap ─ wallet

Endpoints

Method Path Description
POST /v2/register/:session dApp initializes the session with readCap and callbackCap before launching the wallet
POST /v2/callback/:session/:callbackCap Wallet posts the signed result here
GET /v2/stream/:session/:readCap SSE stream; holds connection until result arrives
GET /v2/result/:session/:readCap Polling; returns result or 404 if pending
GET /v1/health, /v2/health Health check

Architecture

Each relay session gets its own Durable Object instance (RelaySession). This ensures the wallet's POST callback and the dApp's SSE stream hit the same isolate, so they share in-memory state. Results are persisted so they survive eviction, accepted once, and deleted after ten minutes.

SSE connections time out after five minutes. A session has at most five listeners. Edge rate limiting applies before a request creates or reaches a Durable Object, with a per-client, per-action key so generating fresh sessions cannot bypass it.

Security model

Protocol v2 uses an init-then-use flow with three URL-safe random values:

  • session: Durable Object routing key. It is not sufficient to read or write a result.
  • readCap: read capability, prefixed with r_, used only by /v2/stream and /v2/result.
  • callbackCap: callback capability, prefixed with c_, used only by /v2/callback.

The dApp first calls POST /v2/register/:session with JSON { "readCap": "r_...", "callbackCap": "c_..." }. The Durable Object validates prefixes and entropy, stores only SHA-256 hashes of both capabilities, rejects re-registration with 409, and sets a ten-minute cleanup alarm. Callback, stream, and poll requests are forbidden until registration exists. Every callback compares sha256(callbackCap) with the stored callback hash. Every stream and poll compares sha256(readCap) with the stored read hash. Mismatches return 403, including arbitrary but correctly shaped r_ or c_ strings.

The split prevents the wallet callback URL from also being a read bearer token. The dApp keeps readCap local and sends only session plus callbackCap to the wallet in the deep-link callback URL. All three values must carry at least 132 bits of entropy before encoding. Do not log capabilities, include them in analytics, or expose them to third parties. @glyph-oss/connect should still validate the returned nonce and request type before an application accepts a result.

Setup

bun install

Development

bun run dev

Deploy

bun run deploy

dApp Usage

import {
  createRelayCapabilities,
  createTransferRequest,
  createEnvelope,
  launchGlyphRequest,
  registerRelaySession,
  subscribeViaRelay,
  relayCallbackUrl,
} from "@glyph-oss/connect";

const request = createTransferRequest({
  type: "transfer",
  dapp: { name: "My App", origin: "https://my.app" },
  to: "UVYAOYTNYCRBVFBHNFIJUEOUEPEDIDUWWEAXKFSJEBJVASCQEROJOVOEEATL",
  amount: "1000",
});

const relay = createRelayCapabilities();
await registerRelaySession(relay);

const envelope = createEnvelope(request, {
  callback: relayCallbackUrl({
    session: relay.session,
    callbackCap: relay.callbackCap,
  }),
});

const resultPromise = subscribeViaRelay(request, {
  session: relay.session,
  readCap: relay.readCap,
});
launchGlyphRequest(envelope);
const result = await resultPromise;

About

Secure Relay v2 bridge for Glyph Wallet dApp callbacks over SSE and Cloudflare Durable Objects.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages