The rule
component §5.2 (PR #27): "protocol on a PUBLIC endpoint MUST therefore be one of HTTP, HTTPS, WS or GRPC; a TCP or UDP endpoint MUST be PRIVATE." Enforced structurally by the schema conditional and exercised by structural-027-public-tcp-endpoint / structural-028-public-grpc-endpoint.
The context the rule did not have
The platform ships public TCP/UDP exposure as a working capability, and #16 did not surface it. A PUBLIC TCP or UDP endpoint is published at a host:port edge address, not a URL: the ingress layer runs an edge-port pool (28000–32000) and allocates one edge port per PUBLIC TCP/UDP endpoint, terminating at the gateway and forwarding over the mesh. The platform's ingress model documents the split directly — "Ingress derives a flat subdomain for PUBLIC HTTP/HTTPS/WS/GRPC bindings and an explicit edge port for PUBLIC TCP/UDP bindings" — and the console renders the edge-port address for those endpoints today.
§5.2's two arguments are both about URLs, and both are answered by the address form:
- "What publishes it speaks the HTTP family." True of the subdomain path; not of the edge-port path, which publishes a raw L4 address and speaks nothing above TCP/UDP. Databases, game servers, MQTT brokers, and SMTP are the workloads this exists for — "expose Postgres publicly" is a real request the platform already satisfies, and one a spec-conformant document cannot express.
- "A
PUBLIC TCP endpoint would compel a probe it has no way to express" (§5.4 requires readiness for a SERVICE exposing a PUBLIC endpoint). This is an argument for scoping the compulsion, not for banning the endpoint: the readiness rule exists because a public URL routes a browser to a not-yet-serving replica and the first request fails without retry. An L4 consumer retries; the rationale §5.4 itself gives for exempting PRIVATE endpoints ("a private consumer inside the mesh retries; a browser does not") applies to a public TCP consumer equally.
Proposal
- Permit
PUBLIC on every protocol. A PUBLIC HTTP/HTTPS/WS/GRPC endpoint publishes a URL; a PUBLIC TCP/UDP endpoint publishes a host:port address. The distinction is worth a sentence in §5.2 because it decides the two rules below.
- Scope §5.4's readiness compulsion to HTTP-family PUBLIC endpoints:
readiness REQUIRED for a SERVICE exposing at least one PUBLIC endpoint whose protocol is HTTP/HTTPS/WS/GRPC. A probe polls an HTTP path, so this also removes the incoherence of compelling a probe against a port that does not speak HTTP.
- Scope endpoint references that derive HTTP addresses to HTTP-family endpoints (semantic): a probe's
endpoint, and a platformDefault's endpoint, MUST name an HTTP-family endpoint — PUBLIC_URL / PUBLIC_HOSTNAME derive from a URL-published endpoint, which a TCP endpoint is not. (Whether a future PUBLIC_ADDRESS source exposes the edge address to the contract can be a separate decision; nothing here requires it.)
- Retire
structural-027-public-tcp-endpoint (or invert it to a pass case) and keep structural-028-public-grpc-endpoint as-is.
Under this shape, primary-endpoint election (§5.2) is unchanged — a sole PUBLIC TCP endpoint can be the primary; only the URL-deriving references exclude it, per rule 3.
Compatibility
Loosening: every document that validates today still validates. The narrowed readiness rule (2) accepts documents the current bundle rejects (a PUBLIC-TCP-only SERVICE without readiness), which is the correct direction inside a major per §3. No released tag exists yet, so the window from #24 applies here too.
The platform will implement rules 2–3 in its own validation ahead of this issue's resolution (they are strictly about coherence of the capability it already ships) and hold its schema at the unrestricted six-protocol PUBLIC set — i.e. it will not adopt the §5.2 restriction. That divergence is tracked by this issue; two conformance cases (structural-027, and the schema-conditional half of the bundle) are expected-fail for the platform until it is resolved.
The rule
component §5.2 (PR #27): "
protocolon aPUBLICendpoint MUST therefore be one ofHTTP,HTTPS,WSorGRPC; aTCPorUDPendpoint MUST bePRIVATE." Enforced structurally by the schema conditional and exercised bystructural-027-public-tcp-endpoint/structural-028-public-grpc-endpoint.The context the rule did not have
The platform ships public TCP/UDP exposure as a working capability, and #16 did not surface it. A
PUBLICTCP or UDP endpoint is published at ahost:portedge address, not a URL: the ingress layer runs an edge-port pool (28000–32000) and allocates one edge port per PUBLIC TCP/UDP endpoint, terminating at the gateway and forwarding over the mesh. The platform's ingress model documents the split directly — "Ingress derives a flat subdomain for PUBLIC HTTP/HTTPS/WS/GRPC bindings and an explicit edge port for PUBLIC TCP/UDP bindings" — and the console renders the edge-port address for those endpoints today.§5.2's two arguments are both about URLs, and both are answered by the address form:
PUBLICTCPendpoint would compel a probe it has no way to express" (§5.4 requiresreadinessfor aSERVICEexposing a PUBLIC endpoint). This is an argument for scoping the compulsion, not for banning the endpoint: the readiness rule exists because a public URL routes a browser to a not-yet-serving replica and the first request fails without retry. An L4 consumer retries; the rationale §5.4 itself gives for exemptingPRIVATEendpoints ("a private consumer inside the mesh retries; a browser does not") applies to a public TCP consumer equally.Proposal
PUBLICon every protocol. APUBLICHTTP/HTTPS/WS/GRPC endpoint publishes a URL; aPUBLICTCP/UDP endpoint publishes ahost:portaddress. The distinction is worth a sentence in §5.2 because it decides the two rules below.readinessREQUIRED for aSERVICEexposing at least onePUBLICendpoint whose protocol is HTTP/HTTPS/WS/GRPC. A probe polls an HTTP path, so this also removes the incoherence of compelling a probe against a port that does not speak HTTP.endpoint, and aplatformDefault'sendpoint, MUST name an HTTP-family endpoint —PUBLIC_URL/PUBLIC_HOSTNAMEderive from a URL-published endpoint, which a TCP endpoint is not. (Whether a futurePUBLIC_ADDRESSsource exposes the edge address to the contract can be a separate decision; nothing here requires it.)structural-027-public-tcp-endpoint(or invert it to a pass case) and keepstructural-028-public-grpc-endpointas-is.Under this shape, primary-endpoint election (§5.2) is unchanged — a sole PUBLIC TCP endpoint can be the primary; only the URL-deriving references exclude it, per rule 3.
Compatibility
Loosening: every document that validates today still validates. The narrowed readiness rule (2) accepts documents the current bundle rejects (a PUBLIC-TCP-only SERVICE without
readiness), which is the correct direction inside a major per §3. No released tag exists yet, so the window from #24 applies here too.The platform will implement rules 2–3 in its own validation ahead of this issue's resolution (they are strictly about coherence of the capability it already ships) and hold its schema at the unrestricted six-protocol
PUBLICset — i.e. it will not adopt the §5.2 restriction. That divergence is tracked by this issue; two conformance cases (structural-027, and the schema-conditional half of the bundle) are expected-fail for the platform until it is resolved.