This document describes the Record structure and its management within the relay system. A Record represents an active session's metadata, associating a unique key with a tunnel session, HTTP transport, client information, and filtering tags. Records are the primary data structure stored in the Store's registry and enable session lookup, routing, and metadata tracking.
For information about the Store implementation that manages the registry of Records, see Store and Session Management. For details on how Records are used during HTTP request processing, see Ingress Handler and HTTP Processing.
The Record type is defined in record.go13-22 and contains all metadata associated with an active session:
| Field | Type | JSON Tag | Purpose |
|---|---|---|---|
Key | string | "key" | Unique identifier (onion ID or TCP port) |
Session | tunnel.Session | "-" | Underlying tunnel session (not serialized) |
RoundTripper | http.RoundTripper | "metrics" | HTTP transport for proxying requests |
Header | tags.Tags | "header" | HTTP headers from the connection upgrade request |
Tags | tags.Tags | "tags" | Query parameters for filtering and metadata |
Since | time.Time | "since" | Session establishment timestamp |
IP | string | "ip" | Client's real IP address |
Path | string | "path" | Original connection path |
The Session field is excluded from JSON serialization (marked with json:"-"), while the RoundTripper is serialized under the key "metrics", allowing metrics from a MetricsTransport wrapper to be exposed.
Record Field Relationships
Sources: record.go13-22
Records are created by the Store.Upsert method in store.go174-212 when a new edge connection is established or an existing session is updated. The creation process captures metadata from the edge.Edge structure:
Record Creation Flow
Sources: store.go174-212
The Upsert method performs the following steps:
Since store.go175edge.Header to tags.Tags store.go176edge.Values to tags.Tags store.go177Record instance store.go178-186RoundTripper from the session store.go188-190RecordMap store.go193-196Ping (if enabled) and Scan goroutines store.go206-209expvars.WebteleportRelaySessionsAccepted store.go211Sources: store.go174-212
The Store maintains a RecordMap field of type map[string]*Record defined in store.go35 This map serves as the primary registry of active sessions, with keys being either:
deriveOnionID(edge.Path)":port" after allocation via freeport.GetFreePort()The map is protected by a sync.RWMutex in store.go32 ensuring thread-safe concurrent access. All mutations to RecordMap occur within the Store.Mut method store.go70-75 which acquires an exclusive lock, executes a mutation function, releases the lock, and triggers an optional OnUpdate callback.
Store RecordMap Structure
Sources: store.go29-37 store.go59-68
The Store provides two primary methods for retrieving Records:
The GetRecord method in store.go140-149 implements host-based lookup with internationalized domain name (IDN) support:
Sources: store.go140-149
The method:
idna.ToASCII store.go142.) store.go143LookupRecord to perform the actual lookup store.go144The LookupRecord method in store.go114-125 provides key-based lookup with alias resolution:
LookupRecord with Alias Resolution
Sources: store.go114-125
The lookup algorithm:
RecordMap store.go116AliasMap for an alias store.go117-118Sources: store.go114-125
The Matches method in record.go24-39 enables filtering Records based on query parameters. This is used by the RecordsHandler in ingress.go53-68 to return only Records matching specific tag criteria.
Matching Algorithm
Sources: record.go24-39
The matching logic implements a subset check:
Tags contains that key record.go27-29true only if all keys and values match record.go38Example usage in RecordsHandler ingress.go56-60:
for _, rec := range all {
if rec.Matches(r.URL.Query()) {
filtered = append(filtered, rec)
}
}
Sources: record.go24-39 ingress.go53-68
A Record transitions through three primary states: creation, active use, and removal.
Record State Diagram
Sources: store.go127-138 store.go174-212 store.go214-238
Records are created during the subscription flow when an upgrader produces a new edge.Edge:
Allocation: store.go151-172
Upsertion: store.go174-212
During active operation, Records serve two concurrent purposes:
Session Monitoring: store.go214-238
Ping: Sends periodic keepalives store.go214-223Scan: Reads control commands ("PONG", "CLOSE") store.go225-238Request Handling: ingress.go45-51
GetRecord or LookupRecordRoundTripper for HTTP proxyingRecords are removed when sessions terminate:
Ping, "CLOSE" command in Scan, or scanner errortunnel.Session identity store.go130expvars.WebteleportRelaySessionsClosed store.go137Sources: store.go127-138 store.go151-172 store.go174-212 store.go214-238
Records integrate with the HTTP processing layer through their RoundTripper field, which is set during creation for HTTP protocol edges store.go188-190:
if edgeProtocol(r) == "http" {
rec.RoundTripper = RoundTripper(r.Session)
}
The RoundTripper function (defined in the transport layer) wraps the tunnel.Session in a MetricsTransport to collect statistics. This enables the relay to:
GetRoundTripper ingress.go45-51The IngressHandler.Dispatch method demonstrates this integration ingress.go118-134:
rt, ok := i.GetRoundTripper(r.Host)
if !ok {
return utils.HostNotFoundHandler()
}
rp := utils.LoggedReverseProxy(rt)
For details on how the RoundTripper adapts tunnel sessions to HTTP transports, see HTTP Tunneling with RoundTripper. For information on metrics collection, see Metrics Transport.
Sources: store.go188-190 ingress.go45-51 ingress.go118-134 record.go16
The relay exposes a RESTful API for retrieving Records at the /records endpoint, implemented by IngressHandler.RecordsHandler in ingress.go53-68 This endpoint:
Matches method ingress.go56-61Query parameters enable filtering by tags. For example:
GET /records?protocol=http returns only HTTP protocol sessionsGET /records?protocol=http&key=value returns sessions matching both criteriaThe JSON response includes all Record fields except Session (which is excluded via json:"-" tag). The RoundTripper field is serialized as "metrics", exposing the MetricsTransport statistics if present.
Sources: ingress.go53-68 record.go13-22
Refresh this wiki