How to Resolve a Short Tailcat Connection Token to a Self-Contained Token
Call ConnBlob.Resolve from the tailscale/tailcat client library to expand a short token containing only a DERP region ID into a self-contained token that embeds full relay details, enabling offline connections without network lookups.
The tailscale/tailcat repository provides peer-to-peer networking tools that use compact ConnBlob tokens to establish connections. When you need to resolve a short Tailcat connection token to a self-contained one, the process involves fetching DERP map details and embedding them directly into the token string, eliminating the need for subsequent network round-trips.
Understanding ConnBlob Token Types
Tailcat utilizes two distinct token formats encoded as ConnBlob strings. A short token contains only a reference to a DERP region ID, requiring the client to fetch the DERP map from the network to discover relay IP addresses. In contrast, a self-contained token embeds the complete DERP relay details—including node IP addresses and ports—directly within the CBOR-encoded payload. This self-contained format allows clients to establish connections offline without additional lookups, significantly improving connection latency.
The Resolution Process in tailcat.go
The Resolve method in tailcat.go (lines 79–102) handles the conversion from short to self-contained tokens. According to the source code implementation, the method executes the following steps:
- Parse the token –
ParseConnBlobdecodes the base64-encoded CBOR payload into aConnInfostructure. - Check for existing relay info – If the
Regionslice is already populated, the token is already self-contained and is returned unchanged. - Expand the region –
ci.Expandretrieves the DERP map via the DERP client and populatesci.Regionwith the full list of relay nodes for the referenced region. - Trim the relay list – To maintain brevity, only the first two relay nodes are retained (
r.Nodes = r.Nodes[:min(2, len(r.Nodes))]). - Re-encode – The updated
ConnInfois serialized back into a newConnBlobusingci.ConnBlob(), which prepends the"tc"prefix and base64-encodes the CBOR data.
The resulting token string is longer than the original but contains all necessary connection information baked directly into the string.
Key Source Files and Implementation Details
The token resolution functionality spans several files in the tailscale/tailcat repository:
tailcat.go– Contains the core implementation, defining theConnBlobtype,ConnInfostructure, and theResolvemethod that orchestrates the expansion process.wire.go– Handles CBOR encoding and decoding of the wire format used byConnBlobtokens, managing the binary serialization of relay metadata.disco.go– Provides discovery utilities used when expanding short tokens to fetch DERP details from the network.cmd/tailcat/tailcat.go– CLI entry point that supports printing full-address tokens via the-full-addressflag and can invoke the resolution logic directly.
Practical Code Examples
The following example demonstrates resolving a short token on the client side:
import (
"context"
"fmt"
"tailscale.com/tailcat"
)
func main() {
shortToken := tailcat.ConnBlob("tcAQAB...") // a short token (region ID only)
// Resolve it to a self-contained token.
resolved, err := shortToken.Resolve(context.Background())
if err != nil {
panic(err)
}
fmt.Println("Resolved token:", resolved)
// Output: Resolved token: tcAQAB... (longer string with embedded DERP info)
}
Once resolved, use the self-contained token to initialize a client without requiring further network lookups:
func startClient(resolvedToken tailcat.ConnBlob) error {
// Parse the resolved token back into ConnInfo.
ci, err := tailcat.ParseConnBlob(resolvedToken)
if err != nil {
return err
}
// Create a client that connects directly using the embedded relay data.
client, err := tailcat.NewClient(ci)
if err != nil {
return err
}
return client.Connect()
}
Summary
- Use
ConnBlob.Resolveintailcat.goto convert short tokens containing only region IDs into self-contained tokens with full relay details. - Short tokens require active DERP map lookups, while self-contained tokens enable offline connection establishment.
- The resolution process automatically trims relay lists to the first two nodes to balance completeness with token size.
- The conversion leverages CBOR encoding via
wire.goand discovery utilities fromdisco.go. - Self-contained tokens are fully backwards compatible and can be parsed by any standard Tailcat client using
ParseConnBlob.
Frequently Asked Questions
What is the difference between a short and self-contained Tailcat token?
A short token contains only a DERP region identifier, requiring the client to fetch relay IP addresses from the network. A self-contained token embeds the complete DERP relay node details—including IP addresses and ports—directly within the base64-encoded CBOR payload, allowing immediate connection without additional lookups.
How does the Resolve method handle tokens that are already self-contained?
According to the implementation in tailcat.go lines 79–102, the method first checks if the Region slice is already populated. If relay information is present, the method returns the original token unchanged, avoiding unnecessary network requests and re-encoding overhead.
Why does the resolved token only include two relay nodes?
The resolution process intentionally trims the relay list to the first two nodes (r.Nodes = r.Nodes[:min(2, len(r.Nodes))]) to maintain a compact token size while preserving redundancy. This optimization ensures the self-contained token remains relatively short while still providing fallback connectivity options.
Can resolved self-contained tokens be used with any Tailcat client?
Yes, once resolved, these tokens are fully compatible with standard Tailcat clients. Parsing the token with ParseConnBlob yields a ConnInfo structure containing all embedded relay data, allowing the client to establish connections via NewClient and Connect without requiring DERP resolution capabilities or network access for map fetching.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →