Chapter 7 / 51
Volume 2 · Configuration & Assembly

xs.json in depth · service-level fields

xs.json is XServer's single assembly surface. This chapter covers every preset field of services[i] — not just "how to write it" but "what happens when you write it wrong".

Overview

Positioning: volume 2 starts at the service level because it decides "how many listeners live in the process and which protocol each speaks". This chapter pairs with the field reference: the tutorial explains principles and boundaries, the reference is for quick lookup. Readers who have run the demo can skim 7.2 / 7.4.

7.1 Intro

Config systems suffer two common diseases. One is looseness — a misspelled field is silently ignored until production bites. The other is scatter — behavior spread across config files, environment variables and command lines. xs.json's answer is fail-fast plus a single source: every preset field that is invalid is named and rejected at startup, and every non-preset field flows untouched into Custom for the application — a field the host doesn't know is never swallowed and never guessed at.

7.2 The five protocol classes (class)

classSemanticsScript callbacks
httpHTTP/1.1 + HTTPS (TLS switch)RequestProc
wsWebSocket + WSSWsOpen/Text/Binary/Ping/Pong/Close
tcpRaw stream / TLS streamEventOpen/Data/Close
udpDatagrams (no TLS)EventDgram
customEvent-driven custom serviceApplication-defined (via the Engine entry point)

Values outside the whitelist fail fast. Picking is simple: what a browser speaks → http/ws; byte streams → tcp; connectionless → udp; everything else, roll your own.

7.3 Endpoint fields

Endpoint field combination
{
    "ip": "0.0.0.0",        /* listen address, default all interfaces */
    "port": 80,             /* plaintext port, required unless custom */
    "tls": true,            /* enable the TLS port; forbidden on udp */
    "port_tls": 443,        /* default 443; colliding with the plaintext port on the same IP is rejected */
    "ip_tls": "0.0.0.0"     /* defaults to ip */
}

What just happened: a service has at most two endpoints (plaintext + TLS). The collision rule is a startup check — the plaintext and TLS ports clashing on the same IP is rejected outright; there is no silent "the later one wins". Certificate paths are not service-level: they live at the host level (chapter 8); the service level only decides "whether a TLS port exists".

7.4 Limit fields

FieldPurposeConstraint
backlogListen backlog (passed through to xrt)uint32 range validation
recv_limitPer-read stream cap≤SIZE_MAX; on udp ≤65535

recv_limit is a memory defense line: the cap on data pulled in per read. On tcp it shapes the per-connection buffer peak; on udp datagrams are naturally ≤64KB, so the cap tightens to 65535. Request-level header/body limits are not preset service fields — they are Custom knobs (chapter 9) read by the http driver.

7.5 Service-level script defaults

Five script-related fields may be written at the service level as defaults for all hosts:

Service-level script defaults → synthesized into DefaultHost
{
    "class": "http",
    "name": "my-site",
    "port": 8080,
    "path": "wwwroot",       /* static root */
    "devlang": "c",          /* script language */
    "devfile": "main.c",     /* script entry */
    "dev_inc": "includes",   /* extra header dirs (semicolon-separated) */
    "dev_lib": "librarys"    /* extra library dirs */
}

When host_default is absent, these five fields synthesize the DefaultHost — that is why chapter 4's minimal app needed no host_default. Host-level fields with the same names override item by item.

7.6 Contract

  • Validation: the class whitelist, port 1–65535, tls×udp mutual exclusion, port collisions — all named and fail-fast at startup.
  • Defaults: service-level script fields are only a default source for the host level, not a second config system.
  • Uniqueness: name is globally unique — it is the runtime key for xsServerFind and the target descriptor of reloads.

Common mistakes

A port collision mistaken for "environment problems"
Symptom
Startup fails; the error names a plaintext/TLS port collision.
Cause
port and port_tls are equal on the same IP (or each collides with other services).
✗ Wrong
{ "ip": "0.0.0.0", "port": 8080,
  "tls": true, "port_tls": 8080 }
✓ Right
{ "ip": "0.0.0.0", "port": 8080,
  "tls": true, "port_tls": 8443 }
TLS configured on udp
Symptom
Startup fails: tls on udp.
Cause
UDP is a datagram protocol; XServer does not wrap TLS over UDP — secure UDP transport is the application's business (e.g. build a DTLS-style scheme yourself).
✗ Wrong
{ "class": "udp", "port": 9000,
  "tls": true }
✓ Right
{ "class": "udp", "port": 9000 }

Exercises

BasicStart one of each class

Write an xs.json containing four services — http (8080), ws (8081), tcp (8082), udp (8083) — all runnable on the default variant (scripts may export nothing).

Acceptance: four bound lines in the boot log; Ctrl+C exits gracefully.
AdvancedTrigger every endpoint validation

Create in turn: port out of range, missing port, plaintext/TLS collision, tls on udp — record each error message.

Acceptance: all four messages map to the rules in 7.3; none is a "vague failure".
ChallengeDual-endpoint observation under load

Enable the TLS port on an http service with a certificate (the home repository's test certs work), hit both endpoints with curl and observe the handshake differences.

Acceptance: you can explain what the TLS endpoint does with a plaintext request (hint: the bytes enter TLS parsing as a stream; chapter 22 builds on this).

Summary

PointConclusion
Five classeshttp / ws / tcp / udp / custom; whitelist fail-fast
EndpointsPlaintext + TLS dual endpoints; same-IP collision rejected
Limitsbacklog passes through to xrt; recv_limit on udp ≤65535
Script defaultsFive service-level fields synthesize the DefaultHost; host level overrides item by item
Quick lookupthe ref-config field reference
xrt further reading