> ## Documentation Index
> Fetch the complete documentation index at: https://gomoqt.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Session

A MOQ Session is established when a client dials a server (via `moqt.Dialer`) or a server accepts a connection (via `moqt.Handler`). The session manages subscriptions, announcements, fetches, and probes over the underlying QUIC connection.

## Implementation

### `moqt.Session`

```go theme={null}
type Session struct {
    // contains filtered or unexported fields
}

func (s *Session) Subscribe(ctx context.Context, path BroadcastPath, name TrackName, config *SubscribeConfig) (*TrackReader, error)
func (s *Session) AcceptAnnounce(prefix string) (*AnnouncementReader, error)
func (s *Session) Fetch(req *FetchRequest) (*GroupReader, error)
func (s *Session) TrackInfo(ctx context.Context, path BroadcastPath, name TrackName) (*PublishInfo, error)
func (s *Session) Probe(targetBitrate uint64) (<-chan ProbeResult, error)
func (s *Session) ProbeTargets() <-chan ProbeResult
func (s *Session) Stats() SessionStats
func (s *Session) CloseWithError(code SessionErrorCode, msg string) error
func (s *Session) Context() context.Context
func (s *Session) ConnectionState() ConnectionState
func (s *Session) LocalAddr() net.Addr
func (s *Session) RemoteAddr() net.Addr
```

Outgoing requests such as subscribing to tracks, fetching specific groups, probing bitrate, or discovering available tracks are handled by the session.

## Request URI

`Session.RequestURI` returns the session's request URI as received, like Go's
`http.Request.RequestURI` — the path, plus `?` and the query when there is one, with
no scheme or host — on either transport binding and on either side of the session:

```go theme={null}
func (h *HandlerImpl) ServeMOQ(sess *moqt.Session) {
    path := sess.RequestURI()
    // path == "/live/alice" for a client that dialed
    // moqt://host/live/alice or https://host/live/alice
}
```

When the request has a query, it follows the path after `?`, as in a request URI:
`"/live/alice?jwt=x"`. Split them with `url.Parse`:

```go theme={null}
u, err := url.Parse(sess.RequestURI())
if err != nil {
    return
}
token := u.Query().Get("jwt") // u.Path == "/live/alice"
```

A query commonly carries a credential, so avoid logging the whole value.

The path is resolved by the binding before the session is constructed, so it is
available immediately — you never wait on the peer's SETUP to read it:

* **WebTransport:** the path is the HTTP request's path and query as received
  (`r.URL.RequestURI()`), resolved by the HTTP handshake. It is also still available directly on the `*http.Request` in
  your `http.Handler`, which is where you route with `http.ServeMux`.
* **Native QUIC:** native QUIC has no handshake-time request URI, so the client
  conveys the path in its SETUP message, as the mandatory Path parameter, with `?`
  and the query appended when there is one. A
  server-side router reads it before constructing the session; a SETUP that omits
  it, or whose path is not rooted at `/`, is rejected.

A path is always rooted at `/` — dialing `moqt://host` or `https://host` with no
path yields `"/"`. `RequestURI` returns `""` only for a session constructed without a
binding-supplied path, which the `Dialer` and `Server` entry points never produce.

<Note>
  `Session.RequestURI` is the session's request URI, which selects a server-side
  endpoint. It is unrelated to `BroadcastPath`, which identifies a broadcast
  *within* an established session — which is why the accessor is named after the
  request URI and not `Path`: in this package "path" already means `BroadcastPath`.
</Note>

## Connection State

After a session is established, you can retrieve connection metadata via `ConnectionState()`:

```go theme={null}
    state := sess.ConnectionState()
    fmt.Println("Protocol version:", state.Version) // e.g., "moq-lite-05"
    fmt.Println("TLS state:", state.TLS)
```

The `ConnectionState` struct contains:

| Field | Type | Description |
| - | - | - |
| `Version` | `string` | The negotiated MOQ protocol version (e.g., `"moq-lite-05"`) |
| `TLS` | `*tls.ConnectionState` | TLS connection state when available |

## Connection Statistics

Use `Session.Stats()` to fetch a point-in-time snapshot of the session's observable metrics.

```go theme={null}
stats := sess.Stats()
fmt.Printf("estimated bitrate=%d bps\n", stats.EstimatedBitrate)
fmt.Printf("rtt=%s\n", stats.RTT)
fmt.Printf("bytes sent=%d\n", stats.BytesSent)
fmt.Printf("bytes received=%d\n", stats.BytesReceived)
```

`SessionStats` includes:

| Field | Type | Description |
| - | - | - |
| `EstimatedBitrate` | `uint64` | Latest measured outbound bitrate from the probe mechanism. Zero until a measurement is available. |
| `RTT` | `time.Duration` | Smoothed round-trip time from the underlying transport. Zero when unavailable. |
| `BytesSent` | `uint64` | Total bytes sent on the underlying connection. Zero when unavailable. |
| `BytesReceived` | `uint64` | Total bytes received on the underlying connection. Zero when unavailable. |

## Track Info

`Session.TrackInfo` opens a Track Stream and requests a track's immutable
publisher properties (TRACK\_INFO) — priority, ordering preference, max
latency, and the `Timescale` needed to interpret frame timestamps — without
subscribing to it. The returned properties are fixed for the lifetime of the
track and should be cached by the caller.

```go theme={null}
info, err := sess.TrackInfo(ctx, "/live/alice", "video")
if err != nil {
	// Handle error
}
fmt.Println("Timescale:", info.Timescale)
```

A publisher can customize the properties a subscriber sees in TRACK\_INFO by
implementing `TrackInfoProvider` on its `TrackHandler`; see
[Producing a Track](/moq/produce_track#track-info).

The values are zero when the current transport does not expose the corresponding metrics, such as some WebTransport browser sessions.

## Subscribe to a Track

<Columns cols={2}>
  <Card title="Subscribe to a Track" icon="arrow-up-right-from-square" href="/moq/subscribe#subscribe-to-a-track" />
</Columns>

## Discover Available Broadcasts

<Columns cols={2}>
  <Card title="Discover Broadcasts" icon="arrow-up-right-from-square" href="/moq/announce_discover#discover-broadcasts" />
</Columns>

## Fetch a Group

<Columns cols={2}>
  <Card title="Fetch" icon="arrow-up-right-from-square" href="/moq/fetch" />
</Columns>

## Probe Bitrate

<Columns cols={2}>
  <Card title="Probe" icon="arrow-up-right-from-square" href="/moq/probe" />
</Columns>

## Incoming Requests

Incoming requests, such as track subscriptions and discovery broadcasts, are handled internally by the session's `moqt.TrackMux`, not directly by the `moqt.Session` struct. Therefore, there are no dedicated methods for these requests on `moqt.Session`.

### Handle Track Subscriptions

<Columns cols={2}>
  <Card title="Handle Track Subscriptions" icon="arrow-up-right-from-square" href="/moq/publish#handle-track-subscriptions" />
</Columns>

### Announce Broadcasts

<Columns cols={2}>
  <Card title="Announce Broadcasts" icon="arrow-up-right-from-square" href="/moq/announce_discover#announce-broadcasts" />
</Columns>

## Terminating a Session

To explicitly close a session due to protocol violations, errors, or other reasons, use the `Session.CloseWithError` method. This closes all associated streams.

```go theme={null}
func (s *Session) CloseWithError(code SessionErrorCode, msg string) error
```

* `code`: Error code (e.g., from built-in codes)
* `msg`: Descriptive message

Prefer reserved error codes for standard reasons. See [Built-in Error Codes](/moq/errors#built-in-error-codes) for details.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.