Integration
Mobile Approval
QR challenge-response approval for high-risk operations. No PINs, no biometrics — scanning grants nothing. The challenge is the authorization.
Protocol Overview
1. RTQ generates a challenge (unique, time-bound, operation-specific)
2. Challenge encoded as QR code displayed to user
3. User scans QR with mobile device
4. Mobile device signs the challenge with device key
5. Signed response sent back to RTQ
6. RTQ verifies signature + expiry + operation match
7. If valid → single-use ticket issued → execution proceeds
2. Challenge encoded as QR code displayed to user
3. User scans QR with mobile device
4. Mobile device signs the challenge with device key
5. Signed response sent back to RTQ
6. RTQ verifies signature + expiry + operation match
7. If valid → single-use ticket issued → execution proceeds
Security Properties
Challenge-Response
Scanning the QR alone grants nothing. The mobile device must cryptographically sign the challenge.
Time-Bound
Challenges expire after a configurable timeout (default: 60 seconds).
Operation-Bound
Each challenge is bound to the exact operation (capability name + input hash).
Single-Use
Once a challenge is answered, it cannot be reused.
No PINs
No PINs, passwords, or biometrics are part of the protocol. The device key is the credential.
Device Pairing
Before a mobile device can approve operations, it must be paired with the RTQ runtime. Pairing is a one-time process:
1. RTQ generates a pairing key
2. Pairing key displayed as QR code
3. Mobile app scans and stores the pairing key
4. Subsequent challenges are sent to paired devices only
2. Pairing key displayed as QR code
3. Mobile app scans and stores the pairing key
4. Subsequent challenges are sent to paired devices only
Configuration
const rtq = createRTQ({
signingKey: process.env.RTQ_SIGNING_KEY!,
approval: {
strategy: "qr-mobile",
timeout: 60_000, // 60 seconds
requireApproval: (cap) => cap.risk.base === "high" || cap.risk.base === "critical",
},
});
signingKey: process.env.RTQ_SIGNING_KEY!,
approval: {
strategy: "qr-mobile",
timeout: 60_000, // 60 seconds
requireApproval: (cap) => cap.risk.base === "high" || cap.risk.base === "critical",
},
});