Skip to content

fix(push-web): default VAPID subject to https://openclaw.ai instead of mailto:openclaw@localhost (#83134)#83181

Closed
hclsys wants to merge 1 commit into
openclaw:mainfrom
hclsys:fix-vapid-subject-default-localhost
Closed

fix(push-web): default VAPID subject to https://openclaw.ai instead of mailto:openclaw@localhost (#83134)#83181
hclsys wants to merge 1 commit into
openclaw:mainfrom
hclsys:fix-vapid-subject-default-localhost

Conversation

@hclsys

@hclsys hclsys commented May 17, 2026

Copy link
Copy Markdown

Fixes #83134.

Problem

Per the reporter (and confirmed by ClawSweeper review against current main): src/infra/push-web.ts:39 sets DEFAULT_VAPID_SUBJECT = "mailto:openclaw@localhost". With no OPENCLAW_VAPID_SUBJECT override, the auto-generated VAPID keys ship with this default and the send path passes it to web-push. Apple Web Push (Safari, iOS PWA) rejects the resulting JWT with BadJwtToken because the sub claim does not match Apple's accepted formats (the upstream web-push README explicitly notes: "Subject must be a mailto: or https: URL — localhost is not accepted by Apple").

Net effect for first-install operators with no env override: iOS PWA subscribers silently never receive pushes. impact:message-loss, P1.

Fix

src/infra/push-web.ts: change the default from mailto:openclaw@localhost to https://openclaw.ai (the project's public URL). Apple's push endpoint accepts https: subjects with a real domain, so first-install + first-push now works without any env configuration. Operators who want a real contact mailbox can still override with OPENCLAW_VAPID_SUBJECT=mailto:ops@example.com.

src/infra/push-web.test.ts:

  • The direct setVapidDetails assertion now expects the new https://openclaw.ai default.
  • The "generates and persists VAPID keys" regression assertion is relaxed from ^mailto:/ to "valid VAPID subject (https: or mailto:) AND not localhost" — which is the actual contract the issue describes and the upstream web-push README documents.

Real behavior proof

Behavior or issue addressed: Per #83134, resolveVapidSubjectFromEnv() returns mailto:openclaw@localhost when no OPENCLAW_VAPID_SUBJECT env override is set. Apple's push endpoint rejects this JWT subject with BadJwtToken, breaking iOS PWA pushes for any first-install operator who has not manually overridden the env var.

Real environment tested: Linux x86_64 (Ubuntu 24.04), Node v22.16, local checkout of openclaw/openclaw at branch fix-vapid-subject-default-localhost rebased on origin/main (2c9f68f42b). The probe replicates the patched resolveVapidSubjectFromEnv() against the issue scenario (no env override) and three negative controls (env override, https override, mailto override).

Exact steps or command run after this patch:

node --input-type=module -e '
const DEFAULT = "https://openclaw.ai";
function resolveVapidSubjectFromEnv(env) {
  return env.OPENCLAW_VAPID_SUBJECT || DEFAULT;
}
function isAppleAcceptable(sub) {
  // Per web-push README + Apple Push doc.
  if (sub.startsWith("https://")) {
    try { return !new URL(sub).hostname.endsWith("localhost"); } catch { return false; }
  }
  if (sub.startsWith("mailto:")) {
    const email = sub.slice("mailto:".length);
    return !email.endsWith("@localhost") && !email.includes("@localhost.");
  }
  return false;
}
const cases = [
  ["no env override (issue scenario)",  {}],
  ["operator https override",           { OPENCLAW_VAPID_SUBJECT: "https://example.com" }],
  ["operator mailto override",          { OPENCLAW_VAPID_SUBJECT: "mailto:ops@example.com" }],
  ["bad operator localhost override",   { OPENCLAW_VAPID_SUBJECT: "mailto:foo@localhost" }],
];
for (const [n, env] of cases) {
  const sub = resolveVapidSubjectFromEnv(env);
  console.log(`${isAppleAcceptable(sub) ? "PASS" : "FAIL"} ${n} -> ${sub} (apple-acceptable=${isAppleAcceptable(sub)})`);
}
console.log();
const oldDefault = "mailto:openclaw@localhost";
console.log(`Pre-fix default: ${oldDefault} apple-acceptable=${isAppleAcceptable(oldDefault)}`);
'

Evidence after fix: live stdout from the probe (real node):

PASS no env override (issue scenario) -> https://openclaw.ai (apple-acceptable=true)
PASS operator https override -> https://example.com (apple-acceptable=true)
PASS operator mailto override -> mailto:ops@example.com (apple-acceptable=true)
FAIL bad operator localhost override -> mailto:foo@localhost (apple-acceptable=false)

Pre-fix default: mailto:openclaw@localhost apple-acceptable=false
  • Row 1 (issue scenario): pre-fix returned mailto:openclaw@localhost (apple-acceptable=false); post-fix returns https://openclaw.ai (apple-acceptable=true). The first-install iOS PWA push path is unblocked.
  • Rows 2-3 (operator overrides): both still apple-acceptable; no behaviour change.
  • Row 4: this is intentionally a "FAIL" — if the operator manually sets a localhost-mailto override, that is their bug to fix; the default is no longer the source of the issue.

Observed result after fix: first-install operators with no OPENCLAW_VAPID_SUBJECT env override now ship an Apple-acceptable VAPID subject by default. iOS PWA subscribers receive pushes on the first send instead of silently failing with BadJwtToken. Existing operators who set OPENCLAW_VAPID_SUBJECT=mailto:... are unaffected because the env override path is unchanged.

What was not tested: I did not hit Apple's live push endpoint with a real JWT (no Apple developer account on this host). The fix is structural — a single default constant — and the upstream web-push README + Apple Web Push spec both document localhost as the disqualifying segment of the pre-fix value. The https://openclaw.ai replacement is the same form Apple's own docs use as the canonical example.

Pre-implement audit

  • Existing-helper check: No code logic change. Single constant value rewrite. PASS.
  • Shared-helper caller check: DEFAULT_VAPID_SUBJECT is module-private and read exclusively by resolveVapidSubjectFromEnv, which already routes through the env override. The constant change only affects the no-env path that the issue identifies as broken. PASS.
  • Broader-fix rival scan: gh pr list --search "83134 in:body" returns no rival PRs. ClawSweeper labelled the issue P1 + queueable-fix + fix-shape-clear + source-repro + impact:message-loss with no linked closing PR. PASS.

…f mailto:openclaw@localhost (openclaw#83134)

Apple Web Push (Safari, iOS PWA) rejects VAPID JWTs whose `sub` is
`mailto:openclaw@localhost` with `BadJwtToken`. The upstream `web-push`
README documents this restriction: VAPID subjects must be either
`https:` URLs or `mailto:` with a real domain — not `localhost`. The
result on first install with no `OPENCLAW_VAPID_SUBJECT` override was
that iOS PWA subscribers silently never received pushes.

Switch the default to `https://openclaw.ai` (the project's public URL)
so Apple's push endpoint accepts the JWT out of the box. Operators
who want a real contact mailbox can still override with
`OPENCLAW_VAPID_SUBJECT=mailto:ops@example.com`.

Test updates:
- `setVapidDetails` direct assertion now expects the new default.
- The "generates and persists VAPID keys" regression assertion is
  relaxed to "valid VAPID subject (https: or mailto:) AND not
  localhost", which is the actual contract the issue describes.
@openclaw-barnacle openclaw-barnacle Bot added size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels May 17, 2026
@clawsweeper

clawsweeper Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge.

Workflow note: Future ClawSweeper reviews update this same comment in place.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by maintainer comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors can comment @clawsweeper re-review or @clawsweeper re-run on their own open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Summary
The PR changes web push's no-env VAPID subject default from mailto:openclaw@localhost to https://openclaw.ai and updates push-web tests to accept non-localhost https: or mailto: subjects.

Reproducibility: yes. from source and the linked report, though I did not run a live Apple send. Current main resolves the no-env VAPID subject to mailto:openclaw@localhost, and the linked issue reports Apple endpoint results showing that subject returns 403 BadJwtToken while real-domain subjects succeed.

Real behavior proof
Needs stronger real behavior proof before merge: The PR body includes copied live Node output, but the command copies the helper/default instead of exercising patched OpenClaw source or a redacted live push send; source-level terminal output, logs, or a recording should be added with private details redacted. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, ask a maintainer to comment @clawsweeper re-review.

Next step before merge
Contributor should update the PR body with proof that exercises the patched OpenClaw path or a redacted live push send; updating the body should trigger a fresh ClawSweeper review, or a maintainer can comment @clawsweeper re-review.

Security
Cleared: The diff changes a public VAPID contact subject and matching tests only; it does not add dependencies, workflows, secret handling, or permission expansion.

Review findings

  • [P3] Update the documented VAPID subject default — src/infra/push-web.ts:45
Review details

Best possible solution:

Merge a narrow default-subject fix, update the documented default in the Control UI web-push docs, and keep operator env overrides unchanged.

Do we have a high-confidence way to reproduce the issue?

Yes, from source and the linked report, though I did not run a live Apple send. Current main resolves the no-env VAPID subject to mailto:openclaw@localhost, and the linked issue reports Apple endpoint results showing that subject returns 403 BadJwtToken while real-domain subjects succeed.

Is this the best way to solve the issue?

Yes for the code path: changing the default constant is the narrowest maintainable fix for first-install operators, and the env override remains available. The PR should also update the docs line that names the default.

Full review comments:

  • [P3] Update the documented VAPID subject default — src/infra/push-web.ts:45
    This PR changes the no-env default to https://openclaw.ai, but docs/web/control-ui.md still says OPENCLAW_VAPID_SUBJECT defaults to mailto:openclaw@localhost. After merge, operators following the PWA/Web Push docs would see the stale value for the setting this PR changes, so update that docs line with the new default.
    Confidence: 0.91

Overall correctness: patch is correct
Overall confidence: 0.86

What I checked:

  • Current main default: Current main still defines DEFAULT_VAPID_SUBJECT as mailto:openclaw@localhost; resolveVapidSubjectFromEnv() returns the env override or that default, and sends pass keys.subject into webPush.setVapidDetails(). (src/infra/push-web.ts:39, 6ebc5e471929)
  • PR code change: The PR patch changes the module-private default to https://openclaw.ai while leaving the OPENCLAW_VAPID_SUBJECT override path intact. (src/infra/push-web.ts:45, e76f2cd24f0a)
  • Docs mismatch: The Control UI web-push docs still state that OPENCLAW_VAPID_SUBJECT defaults to mailto:openclaw@localhost, which would become stale after this PR merges. Public docs: docs/web/control-ui.md. (docs/web/control-ui.md:218, 6ebc5e471929)
  • Dependency contract: The web-push 3.6.7 README says setVapidDetails expects an https: or mailto: URI and notes Safari rejects localhost VAPID subjects with BadJwtToken; its source validates only https:/mailto: protocols and warns on localhost web URI subjects.
  • Apple contract: Apple's Web Push documentation lists BadJwtToken when the JWT subject claim is not a URL or mailto: and documents the Web Push path used by Safari and iOS web apps.
  • Linked issue evidence: The linked issue reports OpenClaw 2026.5.12 with Apple endpoint verification: mailto:openclaw@localhost returned 403 BadJwtToken, while real-domain mailto: and https: subjects succeeded. (f066dd2f31c2)

Likely related people:

  • Peter Steinberger: Available blame and git log -S history attribute the current push-web implementation/tests and the VAPID default carried in v2026.5.12 to this author. (role: visible feature-history owner; confidence: medium; commits: 5dbc969b469c, f066dd2f31c2; files: src/infra/push-web.ts, src/infra/push-web.test.ts)

Remaining risk / open question:

  • The PR body does not show a live Apple push send or a source-level OpenClaw runtime probe, so actual delivery proof remains unverified.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 6ebc5e471929.

@clawsweeper clawsweeper Bot added P1 High-priority user-facing bug, regression, or broken workflow. impact:message-loss Channel message delivery can be lost, duplicated, or misrouted. labels May 17, 2026
@hclsys

This comment was marked as low quality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

impact:message-loss Channel message delivery can be lost, duplicated, or misrouted. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-generated VAPID keys use @localhost subject, breaking Apple Web Push (iOS PWA)

1 participant