diff --git a/server/src/__tests__/invite-onboarding-text.test.ts b/server/src/__tests__/invite-onboarding-text.test.ts index 5f1f8db5..914f092e 100644 --- a/server/src/__tests__/invite-onboarding-text.test.ts +++ b/server/src/__tests__/invite-onboarding-text.test.ts @@ -46,6 +46,8 @@ describe("buildInviteOnboardingTextDocument", () => { expect(text).toContain("http://localhost:3100"); expect(text).toContain("host.docker.internal"); expect(text).toContain("paperclipApiUrl"); + expect(text).toContain("You MUST include agentDefaultsPayload.headers.x-openclaw-auth"); + expect(text).toContain("will fail with 401 Unauthorized"); expect(text).toContain("set the first reachable candidate as agentDefaultsPayload.paperclipApiUrl"); }); diff --git a/server/src/routes/access.ts b/server/src/routes/access.ts index 0db3afbc..4eda1ad7 100644 --- a/server/src/routes/access.ts +++ b/server/src/routes/access.ts @@ -427,6 +427,22 @@ function normalizeAgentDefaultsForJoin(input: { normalized.webhookAuthHeader = defaults.webhookAuthHeader.trim(); } + const openClawAuthHeader = headers ? headerMapGetIgnoreCase(headers, "x-openclaw-auth") : null; + if (openClawAuthHeader) { + diagnostics.push({ + code: "openclaw_auth_header_configured", + level: "info", + message: "Gateway auth token received via headers.x-openclaw-auth.", + }); + } else { + diagnostics.push({ + code: "openclaw_auth_header_missing", + level: "warn", + message: "Gateway auth token is missing from agent defaults.", + hint: "Set agentDefaultsPayload.headers.x-openclaw-auth to the token your OpenClaw /v1/responses endpoint requires.", + }); + } + if (isPlainObject(defaults.payloadTemplate)) { normalized.payloadTemplate = defaults.payloadTemplate; } @@ -646,7 +662,7 @@ function buildInviteOnboardingManifest( invite: toInviteSummaryResponse(req, token, invite), onboarding: { instructions: - "Join as an agent, save your one-time claim secret, wait for board approval, then claim your API key and install the Paperclip skill before starting heartbeat loops.", + "Join as an OpenClaw agent, save your one-time claim secret, wait for board approval, then claim your API key and install the Paperclip skill before starting heartbeat loops. You MUST include agentDefaultsPayload.headers.x-openclaw-auth in your join request so Paperclip can authenticate callback requests.", inviteMessage: extractInviteMessage(invite), recommendedAdapterType: "openclaw", requiredFields: { @@ -655,7 +671,7 @@ function buildInviteOnboardingManifest( adapterType: "Use 'openclaw' for OpenClaw streaming agents", capabilities: "Optional capability summary", agentDefaultsPayload: - "Optional adapter config such as url/method/headers/webhookAuthHeader and paperclipApiUrl for OpenClaw SSE endpoint", + "Adapter config for OpenClaw SSE endpoint. MUST include headers.x-openclaw-auth; also include url/method/paperclipApiUrl (and optional webhookAuthHeader/timeoutSec/payloadTemplate).", }, registrationEndpoint: { method: "POST", @@ -754,6 +770,9 @@ export function buildInviteOnboardingTextDocument( "## Step 1: Submit agent join request", `${onboarding.registrationEndpoint.method} ${onboarding.registrationEndpoint.url}`, "", + "IMPORTANT: You MUST include agentDefaultsPayload.headers.x-openclaw-auth with your gateway token.", + "Without this token, Paperclip callback requests to your OpenClaw endpoint will fail with 401 Unauthorized.", + "", "Body (JSON):", "{", ' "requestType": "agent",',