Skip to content

Commit 54bfbbc

Browse files
adrians5jclaude
andcommitted
docs(self-hosted): reword server-flavour comments to read naturally
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f6200a0 commit 54bfbbc

5 files changed

Lines changed: 37 additions & 34 deletions

File tree

packages/api-event-handler-server/src/createWebinyApiHandler.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,17 @@ export function createWebinyApiHandler(config: CreateWebinyApiHandlerConfig) {
131131
// Attach the WebSockets upgrade handler to the running HTTP server, backed by the shared
132132
// (root) connection manager so request-time sends reach the live sockets.
133133
//
134-
// Authenticate each WebSocket upgrade from its `?token` JWT so the connection is
135-
// registered under the real identity — required for targeted server→client sends
136-
// (SendToIdentity matches connections by identity id). AuthenticationContext lives in the
137-
// PER-REQUEST stack (registered by ApiCoreFeature, not registerRootStorage), so it isn't
138-
// resolvable from the root container. We build a throwaway request-scoped child container
139-
// (the same way createHandler builds one per HTTP request) and resolve it there. Only the
140-
// CORE stack is needed — no transports — since this is purely the token→identity step
141-
// (the SAME one the HTTP stack runs via RequestIdentityLoader), and it's tenant-independent
142-
// (the HTTP stack establishes identity BEFORE tenant), so it needs no request/tenant state.
143-
// Connections are infrequent (one per admin session), so the per-connection child is cheap.
134+
// Each upgrade is authenticated from its `?token` JWT so the connection is registered
135+
// under the real identity — targeted server→client sends need this, since SendToIdentity
136+
// matches connections by identity id. The catch is that AuthenticationContext lives in the
137+
// per-request stack (registered by ApiCoreFeature, not registerRootStorage), so we can't
138+
// resolve it from the root container. Instead we spin up a short-lived request-scoped
139+
// child container, the same way createHandler does for each HTTP request, and resolve it
140+
// there. We only need the core stack, not the transports, because this is just the
141+
// token→identity step the HTTP stack already runs via RequestIdentityLoader — and that step
142+
// doesn't depend on the tenant (the HTTP stack resolves identity before tenant), so there's
143+
// no request or tenant state to set up. Connections are rare (one per admin session), so a
144+
// fresh child per connection is cheap enough.
144145
const authenticate = async (token: string) => {
145146
const child = rootContainer.createChildContainer();
146147
child.registerInstance(RequestContainer, child);

packages/api-event-handler-server/src/scheduler/ScheduledActionRecoverRoute.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ class ScheduledActionRecoverRouteImpl implements HttpRoute.Interface {
6464
await schema.build(ctx);
6565
}
6666

67-
// Boot recovery runs with no identity (no request), so bypass authorization for the list —
68-
// the same escape hatch ExecuteScheduledActionUseCase uses on the run route. Without this,
69-
// ListScheduledActions' permission check against the anonymous identity fails "Not authorized!".
67+
// At boot there's no request and no identity, so we skip authorization for the list — the
68+
// same thing ExecuteScheduledActionUseCase does on the run route. Without it, the
69+
// permission check in ListScheduledActions sees the anonymous identity and fails with
70+
// "Not authorized!".
7071
const listResult = await this.container
7172
.resolve(IdentityContext)
7273
.withoutAuthorization(() =>

packages/api-event-handler-server/src/scheduler/schedulerServer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ export function registerSchedulerServer(container: Container): void {
4848
headers: { "content-type": "application/json", [SCHEDULER_HEADER]: token },
4949
body: JSON.stringify({ id, namespace, tenant })
5050
});
51-
// fetch only rejects on network errorsa 403/500 comes back as a non-ok response,
52-
// so surface it explicitly (otherwise a failed run is invisible).
51+
// fetch only throws on network errors; a 403 or 500 comes back as a normal non-ok
52+
// response, so we check for that explicitly otherwise a failed run would be silent.
5353
if (!res.ok) {
5454
const body = await res.text().catch(() => "");
5555
console.error(
@@ -98,8 +98,8 @@ export async function startSchedulerServer(rootContainer: Container): Promise<vo
9898
body: JSON.stringify({})
9999
});
100100
const body = await res.json().catch(() => ({}) as Record<string, unknown>);
101-
// fetch only rejects on network errorsa 403/500 comes back as a non-ok response, so
102-
// surface it (a silent 403 on a token mismatch was invisible before).
101+
// fetch only throws on network errors; a 403 or 500 comes back as a normal non-ok
102+
// response, so we check for it here (a token-mismatch 403 used to fail silently).
103103
if (!res.ok) {
104104
console.error(`[scheduler] boot recovery failed: HTTP ${res.status}`, body);
105105
return;

packages/api-websockets-server/src/connectionManager/ServerConnectionManager.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ export class ServerConnectionManagerImpl implements WebsocketsConnectionManager.
1717
headers: params.headers
1818
});
1919

20-
// Persist the connection to the shared registry. This is what makes the live socket
21-
// addressable: `SendToIdentity`/`ListConnections` query the registry by identity id, then
22-
// resolve the in-memory socket here by connection id. Without this row the socket exists but
23-
// no identity→connectionId mapping does, so targeted server→client sends reach nothing.
24-
// `connectedOn` is an ISO string (the registry filters connections lexicographically against
25-
// a recency cutoff), so the epoch ms `connectedAt` is converted here.
20+
// Save the connection to the shared registry — this is what lets us actually reach the
21+
// socket later. SendToIdentity and ListConnections look connections up in the registry by
22+
// identity id and then come back here to find the in-memory socket by connection id. If we
23+
// skip this, the socket is in memory but nothing maps an identity to it, so targeted
24+
// server→client sends find nobody. The registry stores `connectedOn` as an ISO string (it
25+
// compares connections against a recency cutoff), so we convert the epoch-ms `connectedAt`.
2626
await this.registry.register({
2727
connectionId: params.connectionId,
2828
identity: params.identity,

packages/api-websockets-server/src/server/WebsocketsServer.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,12 @@ class WebsocketsServer implements IWebsocketsServer {
152152
const host = request.headers.host || "localhost";
153153
const headers = toHeaders(request.headers);
154154

155-
// Register the connection with its real identity (decoded from the `?token` JWT). This
156-
// is async — authentication + the registry write both are — but the socket handlers
157-
// below are wired synchronously so no early close/message is lost during the gap. A
158-
// message arriving before registration completes is simply dropped by the
159-
// `getMetadata` guard (harmless; the client re-sends nothing at connect time).
155+
// Register the connection under its real identity, decoded from the `?token` JWT. Both
156+
// the authentication and the registry write are async, so we kick this off without
157+
// awaiting it and wire the socket handlers below synchronously — that way we don't miss a
158+
// close or message that arrives during the gap. If a message does arrive before
159+
// registration finishes, the `getMetadata` guard below just drops it, which is fine since
160+
// the client doesn't send anything meaningful right at connect time.
160161
void this.registerConnection({
161162
connectionId,
162163
socket,
@@ -199,12 +200,12 @@ class WebsocketsServer implements IWebsocketsServer {
199200
}
200201

201202
/**
202-
* Authenticate the upgrade (from its `?token`/`?tenant` query, set by the app-websockets client)
203-
* and register the live socket in the shared connection manager under the resolved identity +
204-
* tenant. Both the identity AND the registry row are load-bearing for targeted server→client
205-
* sends: `SendToIdentity` looks up connections by identity id in the registry, so a connection
206-
* registered anonymously (or never registered) can't be reached. Fully guarded — a failure here
207-
* must never crash the server or the upgrade; the connection just stays unaddressable.
203+
* Authenticates the upgrade using the `?token`/`?tenant` query the app-websockets client sends,
204+
* then registers the live socket in the shared connection manager under the resolved identity and
205+
* tenant. Getting both right matters for targeted server→client sends: `SendToIdentity` finds
206+
* connections by identity id in the registry, so a connection that's anonymous (or never made it
207+
* into the registry) can't be reached. Everything here is guarded — if something goes wrong we
208+
* don't crash the server or the upgrade, the connection just ends up unaddressable.
208209
*/
209210
private async registerConnection(params: {
210211
connectionId: string;

0 commit comments

Comments
 (0)