Reference

Configuration

The control API's environment, the relay's flags, scope file, systemd unit and sysctl, and the ingest gateway's flags and keys file.

Taken from crates/nearglass-control-api (src/bin/nearglass-control-api.rs, deploy/), relay/ (the unit, the sysctl file, scope.example.json), crates/nearglass-ingest/src/cli.rs and the moq-relay-ietf --help of the relay package, on commit 79b96c7 of the SDK repository. The flag tables for the relay and the gateway are what the built binaries print.

On the managed service you configure none of this: you hold keys and call the control API. This page is for a deployment you run yourself. Command line flags for nearglass itself are in the CLI reference.

Every service runs as a systemd unit on a Linux host, with TLS ended in front of the control API (by tailscale serve or your own front). There are no containers.

The control API

nearglass-control-api serve reads its settings from the environment. Under systemd that is an EnvironmentFile= named control-api.env under /etc, mode 600, owned by root. Hashes and file paths only: no key is ever written there. A bad value stops the service at start with a message that names the variable.

nearglass-control-api serve
nearglass-control-api keygen secret|ingest|publishable|operator|meter

keygen prints a new key once, and the SHA-256 line that the environment file takes. The service never sees a secret or ingest key in plain text.

Not every deployment installs this binary: the managed service runs it for you.

Required

The service refuses to start without these.

VariableWhat
NEARGLASS_API_STATE_FILEthe state file (streams, tenants, keys by hash). Under the unit: /var/lib/nearglass-control-api/state.json
NEARGLASS_API_SECRET_KEY_SHA256 or NEARGLASS_API_OPERATOR_KEY_SHA256at least one: hashes of the default tenant's secret keys, or of the operator keys (for tenants). Comma separated SHA-256 hex digests, not keys
NEARGLASS_API_ISSUER_KEY_FILEwith NEARGLASS_API_SECRET_KEY_SHA256: the default tenant's token signing key, P-256 PKCS#8 PEM
NEARGLASS_API_RELAYScomma separated relay URLs for players and publishers. Not needed when NEARGLASS_API_FLEET_FILE is set
NEARGLASS_API_TENANT_KEY_DIR, NEARGLASS_API_SCOPE_FILEwith an operator key: where tenants' signing keys live (one file each, mode 0600), and the relay scope file the API writes

Network and keys

VariableDefaultWhat
NEARGLASS_API_LISTEN127.0.0.1:8790address to listen on. Keep it on loopback and end TLS in front
NEARGLASS_API_PUBLIC_URLtaken from NEARGLASS_API_HLS_URL when that is this API's own edge, else nonethis API's public address (https://api.nearglass.com, no query). Seated answers name the seat's play_url under it. The player's token-url mode needs it
NEARGLASS_API_TRUST_FORWARDEDoff1: take the client address from X-Forwarded-For. Set it behind a proxy so the per-address rate limit counts viewers, not the proxy
NEARGLASS_API_INGEST_KEY_SHA256nonehashes of the ingest gateway's keys (ik_...)
NEARGLASS_API_PUBLISHABLE_KEYSnonethe publishable keys themselves (pk_...), comma separated: they are not secret. A key without the pk_ prefix is refused
NEARGLASS_API_METER_KEY_SHA256nonehashes of the keys nearglass-meter posts records with
NEARGLASS_API_OPERATOR_KEY_SHA256nonehashes of the operator keys that manage tenants

Tokens and relays

VariableDefaultWhat
NEARGLASS_API_ISSUERnearglass-tokenthe token issuer claim. Must match the relay's scope
NEARGLASS_API_AUDIENCEnearglass-relaythe audience claim. Must match the relay's scope
NEARGLASS_API_VIEWER_TTL120viewer token lifetime, seconds. At most 3600
NEARGLASS_API_PUBLISHER_TTL3600publisher token lifetime, seconds. At most 7 days
NEARGLASS_API_INGEST_TOKEN_TTL86400the gateway's publisher tokens, seconds. At most 7 days
NEARGLASS_API_WATCH_GRACE600seconds. Must equal the relay scope's watch_grace_secs: the player's session renewal depends on it
NEARGLASS_API_NAMESPACE_PREFIXlivethe first segment of a stream's default namespace, live/<id>
NEARGLASS_API_RELAY_CERT_SHA256nonethe relay certificate's SHA-256, hex. Only for a relay with a self-signed certificate
NEARGLASS_API_RELAY_ADMIN_URLnonethe relay's admin port, to close the sessions of lost seats
NEARGLASS_API_RELAY_ADMIN_KEY_FILEnonethe admin port's key file. The URL and the key file go together
NEARGLASS_API_SCOPE_BASEnonethe operator's part of the relay scope file: the default scope and any others, with inline keys
NEARGLASS_API_SCOPE_CLOCK_SKEW2seconds: clock_skew_secs in tenants' scopes. Small, so a controller's turn ends close to its end
NEARGLASS_API_SCOPE_TTL10seconds: ttl_secs in tenants' scopes; how soon a changed limit reaches a scope in use

Without NEARGLASS_API_RELAY_ADMIN_URL the API logs a warning: a lost seat's open sessions then run until the relay's watch grace ends.

Addresses shown to customers

VariableWhat
NEARGLASS_API_RTMP_URLthe gateway's RTMP address, shown when a stream is created (rtmp://ingest.nearglass.com:1935/live)
NEARGLASS_API_SRT_URLthe gateway's SRT address (srt://ingest.nearglass.com:9000)
NEARGLASS_API_HLS_URLthe LL-HLS playlist in every viewer descriptor. {namespace} and {id} are filled in. With the API's own edge: https://api.nearglass.com/v1/play/{id}/hls/index.m3u8
NEARGLASS_API_HLS_ORIGINthe LL-HLS packager on this host (http://127.0.0.1:8888/hls/). Seats are then served and metered through /v1/play/{id}/hls/

Tenants and limits

VariableDefaultWhat
NEARGLASS_API_TENANT_VIEWER_CAP200a new tenant's seats over all its streams
NEARGLASS_API_TENANT_MAX_STREAMS10a new tenant's streams
NEARGLASS_API_TENANT_MAX_EGRESS_MBPSset by the operatora new tenant's send-rate ceiling
NEARGLASS_API_DEFAULT_SPEND_CAPset by the operatora new tenant's monthly cap, USD. Not above the maximum
NEARGLASS_API_MAX_SPEND_CAPset by the operatorthe most a tenant may set for itself, USD
NEARGLASS_API_FREE_VIEWER_HOURS, NEARGLASS_API_FREE_GIBset by the operatora month's free allowance per tenant
NEARGLASS_API_COST_PER_GIB, NEARGLASS_API_COST_PER_VIEWER_HOURset by the operatorwhat usage is priced at, USD

A tenant's relay-scope max_sessions is twice its viewer cap plus 20.

Fleet and operator alerts

VariableWhat
NEARGLASS_API_FLEET_FILEregions, origins and edges: viewers get the healthy edges of every region, publishers the home origin. Replaces NEARGLASS_API_RELAYS. Described with regions in the Operate section
NEARGLASS_API_OPERATOR_ALERT_URL_FILEa file whose first line is the operator's alert webhook URL. The URL is a secret: a root-only file, loaded by systemd as a credential. An empty file means no channel
NEARGLASS_API_OPERATOR_ALERT_FORMATdiscord (default), slack, or json signed with the secret file's first line
NEARGLASS_API_OPERATOR_ALERT_SECRET_FILEthe signing secret for the json format
NEARGLASS_API_STATUS_NAMEthe product's name on the status page. Default nearglass

NEARGLASS_API_OPERATOR_ALERT_URL (the URL itself, in the environment) is refused at start, so a secret is never in a process environment.

The unit

crates/nearglass-control-api/deploy/nearglass-control-api.service runs the binary from /opt/nearglass-control-api/ as a dynamic user with no extra privileges, a read-only system, a private /tmp and no home. The signing key and the relay admin key are root-owned files that systemd hands to the service with LoadCredential, so the service reads them from $CREDENTIALS_DIRECTORY (/run/credentials/nearglass-control-api.service/). It restarts on failure after 2 s.

systemctl daemon-reload && systemctl enable --now nearglass-control-api

A minimal environment file for one tenant:

NEARGLASS_API_LISTEN=127.0.0.1:8790
NEARGLASS_API_STATE_FILE=/var/lib/nearglass-control-api/state.json
NEARGLASS_API_ISSUER_KEY_FILE=/run/credentials/nearglass-control-api.service/issuer.key.pem
NEARGLASS_API_SECRET_KEY_SHA256=<sha256 from keygen secret>
NEARGLASS_API_INGEST_KEY_SHA256=<sha256 from keygen ingest>
NEARGLASS_API_PUBLISHABLE_KEYS=<pk_ from keygen publishable>
NEARGLASS_API_RELAYS=https://relay.nearglass.com:443/
NEARGLASS_API_RELAY_ADMIN_URL=http://127.0.0.1:4480
NEARGLASS_API_RELAY_ADMIN_KEY_FILE=/run/credentials/nearglass-control-api.service/relay-admin.key
NEARGLASS_API_RTMP_URL=rtmp://ingest.nearglass.com:1935/live
NEARGLASS_API_SRT_URL=srt://ingest.nearglass.com:9000
NEARGLASS_API_WATCH_GRACE=600
NEARGLASS_API_TRUST_FORWARDED=1

The relay

The relay is moq-relay-ietf with Nearglass's patches, installed by the nearglass-relay Debian package into /opt/nearglass-relay/moq-relay-ietf. The package installs, and does not start, these files:

FileWhat
/opt/nearglass-relay/moq-relay-ietfthe relay
/lib/systemd/system/nearglass-relay.servicethe systemd unit (below)
/etc/sysctl.d/90-nearglass-relay.confsocket buffer limits (below)
/etc/nearglass-relay/scope.example.jsonan example scope file
/usr/share/doc/nearglass-relay/the README and the copyright file

Before it starts it needs /etc/nearglass-relay/cert.pem, key.pem and scope.json, then systemctl enable --now nearglass-relay. It needs inbound UDP on its port (QUIC and WebTransport). It reads only public keys: the signing key stays with the control API.

Relay flags

From moq-relay-ietf --help. Flags marked "Nearglass" are patches this package adds to the upstream relay.

FlagDefaultWhat
--bind <ADDR>[::]:443listen address (UDP)
--tls-cert <FILE>nonePEM certificate chain. Repeat for several; the first match for the SNI is used, otherwise the last
--tls-key <FILE>nonePEM private key, one for every --tls-cert
--tls-root <FILE>system rootstrust this PEM root for connections to other relays
--tls-disable-verifyoffskip certificate checks. Development and between relays only. Also TLS_DISABLE_VERIFY
--scope-config <FILE>noneJSON file of per-scope token policies. Turns tokens on. Also MOQ_RELAY_SCOPE_CONFIG. Re-read for new sessions; see the scope file
--max-request-id <N>100the largest request id plus one advertised at setup. The default runs out after about 50 viewer sessions in one-to-one mode; the unit sets 1000000
--cache-idle-timeout <SECONDS>30how long to keep a cached track with no subscribers. 0 never evicts. Use 30 for broadcast and 1 for one-to-one streams
--coordinator-file <FILE>/tmp/moq-coordinator.jsonnamespace registrations shared by relays on one host. The unit puts it under /run/nearglass-relay/ so a restart starts clean
--metrics-addr <ADDR>offserve Prometheus metrics at http://<addr>/metrics
--session-log <FILE>offNearglass: append one JSON line per closed token session. This is the metering source. Created if missing
--admin-addr <ADDR>offNearglass: serve the admin port here. Loopback only. Needs --admin-key-file
--admin-key-file <FILE>noneNearglass: the admin port's bearer key, the first line of the file, at least 32 characters. Readable by the relay's user only
--internal-peer <IP>noneNearglass: treat this source address as a peer relay. Repeat. Internal sessions skip token checks, so list only addresses your network authenticates, such as a WireGuard tunnel address
--internal-peer-allow-publicoffNearglass: allow a public --internal-peer address
--cc <bbr|cubic>bbrNearglass: congestion controller for every connection
--ingest-lane <true|false>trueNearglass: run the UDP socket and publishing sessions on their own thread, so an overloaded relay degrades viewers and not ingest
--viewer-send-window-kb <KIB>1024Nearglass: the unsent or unacknowledged bytes held for each public session. 0 is the QUIC library's own limit
--admission <on|observe|off>onNearglass: a saturated relay refuses new viewers and keeps the ones it has. observe measures and refuses nothing. Also NEARGLASS_ADMISSION
--admission-busy <0 to 1>0.85Nearglass: the busy share at which the relay counts as saturated; it admits again 0.15 below. Also NEARGLASS_ADMISSION_BUSY
--admission-retry-ms <MS>2000Nearglass: the retry interval a refusal carries
--setup-rate-per-ip <RATE/BURST>200/1000Nearglass: new connections a second from one address, and the burst. Over it a connection is refused before its TLS handshake. 0 is off. Also NEARGLASS_SETUP_RATE_PER_IP
--setup-rate-global <RATE/BURST>1000/2000Nearglass: the same for the whole relay. Also NEARGLASS_SETUP_RATE_GLOBAL
--announce <URL>noneforward publish announcements to a server for authorization and routing. Without it the relay accepts every unique namespace
--api <URL>, --node <HOST>, --api-url <URL>, --api-ttl <SECONDS>none, none, none, 600upstream's cluster coordination through a moq-api server, in place of the coordinator file
--dev, --qlog-dir, --mlog-dir, --qlog-serve, --mlog-serveoffdevelopment: serve the certificate fingerprint, and write per-connection logs

The relay ignores SIGINT: stop it with SIGTERM.

Environment variables the relay also reads:

VariableDefaultWhat
NEARGLASS_UDP_SNDBUF_KB, NEARGLASS_UDP_RCVBUF_KBderivedUDP socket buffer sizes in KiB. By default the send buffer is sized from the default route's link speed (512 KiB when it is unknown) and the receive buffer is 8 MiB. 0 keeps the kernel default
NEARGLASS_ZEROCOPYoff1: forward each upstream payload to a subscriber by reference instead of copying it
NEARGLASS_GROUP_BOUNDonoff: do not bound the groups in flight per subscription

The relay unit

/lib/systemd/system/nearglass-relay.service as the package installs it. Copy it to /etc/systemd/system/ to adjust the paths and the bind address.

[Service]
ExecStart=/opt/nearglass-relay/moq-relay-ietf \
  --bind 0.0.0.0:443 \
  --tls-cert /etc/nearglass-relay/cert.pem --tls-key /etc/nearglass-relay/key.pem \
  --scope-config /etc/nearglass-relay/scope.json \
  --coordinator-file /run/nearglass-relay/coordinator.json \
  --max-request-id 1000000 \
  --cache-idle-timeout 30 \
  --metrics-addr 127.0.0.1:9090
KillSignal=SIGTERM
Restart=on-failure
RestartSec=2
RuntimeDirectory=nearglass-relay
DynamicUser=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
LimitNOFILE=65536

The unit does not set --admin-addr, --admin-key-file or --session-log. The control API needs the admin port to close lost seats' sessions, and metering needs the session log, so add them for a deployment with seat limits:

  --admin-addr 127.0.0.1:4480 --admin-key-file /etc/nearglass-relay/admin.key \
  --session-log /var/lib/nearglass-relay/sessions.jsonl

The relay runs as a dynamic user, so give it a state directory (StateDirectory=nearglass-relay) for the log, and let it read the key file through LoadCredential.

sysctl

/etc/sysctl.d/90-nearglass-relay.conf (applied on the next boot, or with sysctl --system):

net.core.wmem_max = 8388608
net.core.rmem_max = 8388608

The relay asks for large UDP buffers. Without CAP_NET_ADMIN the kernel caps each request at these two limits, 212992 bytes on a stock Debian or Ubuntu kernel. The relay logs a warning at start when a request was capped, and a UDP socket bound line with the requested and granted sizes.

The scope file

--scope-config names a JSON file of token policies, one per scope. A scope is the URL path a client connects on: https://relay.nearglass.com/tn_abc is the scope /tn_abc. The default entry covers every scope without its own. Scopes in the file require a valid token; the file is re-read for new sessions, so an edit applies once a scope's cached policy expires (ttl_secs). If the file later goes missing or invalid, a scope keeps its cached policy until it expires, then refuses new sessions rather than admitting without a token.

{
  "default": {
    "auth": {
      "keys": [{ "kid": "nearglass-2026-10", "alg": "ES256", "pem_file": "issuer.pub.pem" }],
      "issuers": ["nearglass-token"],
      "audiences": ["nearglass-relay"],
      "clock_skew_secs": 60,
      "ttl_secs": 300
    }
  },
  "scopes": {
    "/tn_abc": { "auth": { "...": "same fields" } }
  }
}
Field in authWhat
keysissuer public keys: kid, alg (ES256), and the key as pem (inline) or pem_file (a path, relative to the scope file)
issuers, audiencesaccepted claims. Match NEARGLASS_API_ISSUER and NEARGLASS_API_AUDIENCE, or nearglass token mint --issuer and --audience
clock_skew_secsallowed clock difference. A publisher session is closed at exp plus this, so keep it small where controller tokens are used
ttl_secshow long a scope's policy is cached
max_sessionslive token sessions in the scope, every role. The next one is closed with "scope full". Omit for no limit; 0 is refused
max_publisherspublishing sessions in the scope
max_egress_mbpsabove this send rate the relay admits no new viewer in the scope. Live ones keep watching
require_token_idrefuse tokens that have no cti. Default off
watch_grace_secsreads (viewers) keep working this long after exp plus skew. At most 3600. 0 (default) is upstream's behaviour. Publishing never outlives exp

When the control API writes this file (NEARGLASS_API_SCOPE_FILE), it takes the operator's NEARGLASS_API_SCOPE_BASE (the default scope and others, with inline pem keys only: a pem_file path would be relative to the wrong directory) and adds one scope per tenant, /tn_<id>, with the tenant's key and limits. A base must not name a /tn_ scope. With no base, the default scope refuses every token, so a path that is not a tenant's is closed.

The admin port

With --admin-addr and --admin-key-file, loopback only. Every request carries the key as Authorization: Bearer <key>.

RequestWhat
POST /close?token_id=<id>[&exp=<unix s>]close the sessions with this token id and refuse the id until its token expires
GET /sessionssession counts per scope
GET /usagethe relay's usage and its admission state, for the node agent

A wrong or missing key is 401.

The session log

One JSON line per closed token session, with these fields: scope, token_id, sub, role, start_ms, end_ms, bytes_out, token_exp and close (closed, error, ended, admin or expired). Refused sessions leave no record. nearglass-meter ships it to the control API.

Relay metrics

With --metrics-addr, at /metrics. Names from the source: moq_relay_seat_refused_total{reason} (scope_full, token_id_in_use, token_closed), moq_relay_seat_sessions, moq_relay_seat_admin_closed_total, moq_relay_seat_expired_total, moq_relay_seat_lapsed_total, moq_relay_admin_denied_total, moq_relay_setup_refused_total and moq_relay_clock_synchronized.

The ingest gateway

nearglass ingest (flags in the CLI reference) takes encoders over RTMP, RTMPS, SRT and WHIP. On a self-hosted deployment run it as a systemd unit beside the relay, with its keys from one of two places:

SourceFlagsKeys come from
the control API--control-api, --control-api-key-filethe API: stream keys and each stream's publisher token, synced every 3 s. A stream deleted in the API stops being admitted
a keys file--keys, --token-filea file you keep, with one publisher token for the streams' namespaces

Give exactly one. With --control-api the gateway starts with the API's list, so the first encoder is not refused for want of a sync.

Ports are whatever you pass: the API only shows customers the addresses in NEARGLASS_API_RTMP_URL and NEARGLASS_API_SRT_URL, so keep those equal to the gateway's --rtmp and --srt. RTMPS and WHIP need a certificate (--rtmps-cert, --rtmps-key, or --whip-cert, --whip-key).

The ingest keys file

nearglass ingest keygen prints the key and one entry. A keys file is JSON with a streams list:

{ "streams": [
  { "name": "studio-a", "key_sha256": "<64 hex>", "namespace": "warp/ingest/studio-a",
    "protocols": ["rtmp", "rtmps", "srt", "whip"] }
] }
FieldRequiredWhat
nameyesthe stream's name, for logs and status. Not secret
key_sha256yesSHA-256 of the key, lowercase hex. Only the hash is stored
namespaceyesthe broadcast namespace, / separated
protocolsyesany of rtmp, rtmps, srt, whip, framed
max_kbpsnobitrate cap in kbit/s. 0 or absent: none. Read but not enforced yet: the gateway accepts a higher bitrate
expiresnoexpiry as Unix seconds. Absent: never
scopenothe relay scope (URL path) to publish on, such as /tn_abc. Absent: the relay URLs as given

One publisher holds a key at a time. A second may take over after the first has been silent for 5 s, which is an encoder reconnecting after a drop.

Environment

The gateway reads RUST_LOG and TLS_DISABLE_VERIFY only. Everything else is a flag.

On this page