← Back to home@KonnectSuite

dsh-host-pairing

AryaAI host pairing: single-use expiring codes exchanged for the browser session cookie, plus the QR offer a desktop client renders (extracted from AryaAI)

Stars
0
Language
TypeScript
Created
Oct 24, 2026
Updated
Oct 24, 2026

Introduction


description: "Host pairing entry point: exchanges a short-lived single-use code for the browser session cookie, and reports the addresses a phone can reach the server at; for users and maintainers of remote access." kind: "package-reference"

@deepseek-ai/dsh-host-pairing

English | 中文

Summary

The Host build aggregate references this package's TypeScript project; route tests pass resolved configuration with an explicit code lifetime.

dsh web authenticates a browser by putting a fresh process token in the URL it prints. That works on the machine running the server and nowhere else: the operator has to carry a long-lived secret to the second device by hand. This package mounts GET /pair?code=…, which exchanges a short-lived single-use code for the same authority-bound session cookie, and GET /pair/offer, which mints a code and reports every address a device on the same private network can reach.

Table of Contents


Use this package

Mount this row beside connection and webserver; nothing else is required. The row is already present in the shipped Web bundle.

Serving a phone also needs a bind the phone can reach. dsh web --host 0.0.0.0 stays refused, because it exposes the server without saying so; dsh web --remote selects the same bind as a stated choice. The offer then reports the tailnet address first, followed by the local-network ones.

The exchange

A device opens the pairing URL, which is http://<authority>/pair?code=<code>. A valid code mints the same cookie GET /?token=… mints — bound to the authority the request arrived on — and redirects to ./. The code is spent by that request whatever the outcome, so a replayed or guessed code cannot be retried against a live entry.

The offer

GET /pair/offer requires a full browser session, because its response hands out a credential; a device that already has one does not need to pair. While the server is bound to loopback the offer reports exposed: false and an empty address list rather than URLs nothing can reach, so a client can say what to change instead of failing at the scanner.

Configuration

codeTtlSeconds bounds a code's absolute lifetime, defaulting to 300 seconds. It is validated on both sides: a code that outlives a walk to the phone is a standing credential, and one that expires mid-scan is unusable.


Understand the implementation

Implementation internals — click to expand

Codes live in memory only, so a restart invalidates every outstanding offer instead of leaving a credential on disk, and redemption is a synchronous compare-and-delete so two requests racing one code cannot both observe it as outstanding. A code is eight characters from an alphabet without the characters a human misreads, drawn from randomBytes with rejection sampling so every symbol is equally likely.

An address is advertised either because it is in the range Tailscale assigns (the RFC 6598 carrier-grade NAT block) or because it is another non-internal IPv4 address this host holds. The range identifies a tailnet address on every platform; matching an interface name instead would miss macOS utun* and Linux tailscale0.

The exchange is the one route that must admit an unauthenticated request, so it applies Connection's Host/Origin fence alone and treats the code as the credential; the fence still refuses an untrusted authority before the code is read, and a refused attempt does not spend the code. The cookie's audience is the request's own Host, so a route admitting a request by another means cannot be made to issue a credential for an authority this server does not serve.


Further Exploration

  • client-connection — owns the browser-session cookie, the trust fence, and the mint this exchange calls.
  • host-webserver — the route carrier both endpoints register on.
  • ui-mobile — the client that requests an offer and renders it as a QR code.

Model Experience

None, as pairing admits a browser session; nothing here reaches a model request.

KV Cache effect

None; this package neither assembles nor sends a provider request.

Known Limitations and Deferred Work

These limits define the current pairing entry point. They are current package constraints, not a task backlog.

  • All interfaces, not one — the webserver accepts only a loopback or an all-interfaces bind, so --remote exposes every interface this host holds, including a public one. The code is the only thing standing between that exposure and the API.
  • A tailnet address, not its name — addresses come from the interface table, so the offer carries the tailnet IPv4 literal. A MagicDNS name would survive an address change and needs the tailscale CLI, which this package does not run.

Dev Note

Working context for maintainers — click to expand

The route spec boots the real webserver through the Loader and drives it over HTTP; the exposed offer branch is covered separately, because a loopback test server cannot report exposed: true without publishing a port on every interface.