mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 03:52:54 +00:00
feat(ios): default to hosted push relay
This commit is contained in:
@@ -73,9 +73,10 @@ Release behavior:
|
||||
- Changing the root gateway version does not change the iOS app version until you explicitly pin from the gateway.
|
||||
- See `apps/ios/VERSIONING.md` for the full workflow.
|
||||
|
||||
Required env for beta builds:
|
||||
Relay behavior for beta builds:
|
||||
|
||||
- `OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com`
|
||||
- Beta builds default to `https://ios-push-relay.openclaw.ai`.
|
||||
- Optional custom relay override: `OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com`
|
||||
This must be a plain `https://host[:port][/path]` base URL without whitespace, query params, fragments, or xcconfig metacharacters.
|
||||
|
||||
Archive without upload:
|
||||
@@ -118,7 +119,7 @@ scripts/ios-asc-keychain-setup.sh \
|
||||
|
||||
This should create `apps/ios/fastlane/.env` with the non-secret ASC variables while the private key stays in Keychain.
|
||||
|
||||
3. Set the official/TestFlight relay URL for the build:
|
||||
3. Optional: set a custom official/TestFlight relay URL for the build. If unset, the beta flow uses `https://ios-push-relay.openclaw.ai`.
|
||||
|
||||
```bash
|
||||
export OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com
|
||||
|
||||
@@ -337,9 +337,9 @@ candidate contains redacted secret placeholders such as `***`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Enable relay-backed push for official iOS builds">
|
||||
Relay-backed push is configured in `openclaw.json`.
|
||||
Relay-backed push uses the hosted OpenClaw relay by default: `https://ios-push-relay.openclaw.ai`.
|
||||
|
||||
Set this in gateway config:
|
||||
To use a custom relay, set this in gateway config:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -373,8 +373,8 @@ candidate contains redacted secret placeholders such as `***`.
|
||||
|
||||
End-to-end flow:
|
||||
|
||||
1. Install an official/TestFlight iOS build that was compiled with the same relay base URL.
|
||||
2. Configure `gateway.push.apns.relay.baseUrl` on the gateway.
|
||||
1. Install an official/TestFlight iOS build.
|
||||
2. Optional: configure `gateway.push.apns.relay.baseUrl` on the gateway only when using a custom relay deployment.
|
||||
3. Pair the iOS app to the gateway and let both node and operator sessions connect.
|
||||
4. The iOS app fetches the gateway identity, registers with the relay using App Attest plus the app receipt, and then publishes the relay-backed `push.apns.register` payload to the paired gateway.
|
||||
5. The gateway stores the relay handle and send grant, then uses them for `push.test`, wake nudges, and reconnect wakes.
|
||||
@@ -387,6 +387,7 @@ candidate contains redacted secret placeholders such as `***`.
|
||||
Compatibility note:
|
||||
|
||||
- `OPENCLAW_APNS_RELAY_BASE_URL` and `OPENCLAW_APNS_RELAY_TIMEOUT_MS` still work as temporary env overrides.
|
||||
- Custom gateway relay URLs must match the relay base URL baked into the official/TestFlight iOS build.
|
||||
- `OPENCLAW_APNS_RELAY_ALLOW_HTTP=true` remains a loopback-only development escape hatch; do not persist HTTP relay URLs in config.
|
||||
|
||||
See [iOS App](/platforms/ios#relay-backed-push-for-official-builds) for the end-to-end flow and [Authentication and trust flow](/platforms/ios#authentication-and-trust-flow) for the relay security model.
|
||||
|
||||
@@ -75,7 +75,9 @@ openclaw gateway call node.list --params "{}"
|
||||
Official distributed iOS builds use the external push relay instead of publishing the raw APNs
|
||||
token to the gateway.
|
||||
|
||||
Gateway-side requirement:
|
||||
By default, official/TestFlight builds and gateways use the hosted relay at `https://ios-push-relay.openclaw.ai`.
|
||||
|
||||
Custom relay deployments can override the gateway relay URL:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -98,7 +100,7 @@ How the flow works:
|
||||
- The iOS app fetches the paired gateway identity and includes it in relay registration, so the relay-backed registration is delegated to that specific gateway.
|
||||
- The app forwards that relay-backed registration to the paired gateway with `push.apns.register`.
|
||||
- The gateway uses that stored relay handle for `push.test`, background wakes, and wake nudges.
|
||||
- The gateway relay base URL must match the relay URL baked into the official/TestFlight iOS build.
|
||||
- Custom gateway relay URLs must match the relay URL baked into the official/TestFlight iOS build.
|
||||
- If the app later connects to a different gateway or a build with a different relay base URL, it refreshes the relay registration instead of reusing the old binding.
|
||||
|
||||
What the gateway does **not** need for this path:
|
||||
@@ -109,7 +111,7 @@ What the gateway does **not** need for this path:
|
||||
Expected operator flow:
|
||||
|
||||
1. Install the official/TestFlight iOS build.
|
||||
2. Set `gateway.push.apns.relay.baseUrl` on the gateway.
|
||||
2. Optional: set `gateway.push.apns.relay.baseUrl` on the gateway only when using a custom relay deployment.
|
||||
3. Pair the app to the gateway and let it finish connecting.
|
||||
4. The app publishes `push.apns.register` automatically after it has an APNs token, the operator session is connected, and relay registration succeeds.
|
||||
5. After that, `push.test`, reconnect wakes, and wake nudges can use the stored relay-backed registration.
|
||||
@@ -128,6 +130,7 @@ compatible but does not count as a durable last-seen update.
|
||||
Compatibility note:
|
||||
|
||||
- `OPENCLAW_APNS_RELAY_BASE_URL` still works as a temporary env override for the gateway.
|
||||
- `OPENCLAW_PUSH_RELAY_BASE_URL` still works as a temporary env override for official/TestFlight iOS builds.
|
||||
|
||||
## Authentication and trust flow
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ set -euo pipefail
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
scripts/ios-beta-prepare.sh --build-number 7 [--team-id TEAMID]
|
||||
|
||||
Optional custom relay:
|
||||
OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com \
|
||||
scripts/ios-beta-prepare.sh --build-number 7 [--team-id TEAMID]
|
||||
|
||||
@@ -26,7 +29,8 @@ VERSION_SYNC_HELPER="${ROOT_DIR}/scripts/ios-sync-versioning.ts"
|
||||
|
||||
BUILD_NUMBER=""
|
||||
TEAM_ID="${IOS_DEVELOPMENT_TEAM:-}"
|
||||
PUSH_RELAY_BASE_URL="${OPENCLAW_PUSH_RELAY_BASE_URL:-${IOS_PUSH_RELAY_BASE_URL:-}}"
|
||||
DEFAULT_IOS_PUSH_RELAY_BASE_URL="https://ios-push-relay.openclaw.ai"
|
||||
PUSH_RELAY_BASE_URL="${OPENCLAW_PUSH_RELAY_BASE_URL:-${IOS_PUSH_RELAY_BASE_URL:-${DEFAULT_IOS_PUSH_RELAY_BASE_URL}}}"
|
||||
PUSH_RELAY_BASE_URL_XCCONFIG=""
|
||||
IOS_VERSION=""
|
||||
|
||||
@@ -118,11 +122,6 @@ if [[ -z "${TEAM_ID}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${PUSH_RELAY_BASE_URL}" ]]; then
|
||||
echo "Missing OPENCLAW_PUSH_RELAY_BASE_URL (or IOS_PUSH_RELAY_BASE_URL) for beta relay registration." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
validate_push_relay_base_url "${PUSH_RELAY_BASE_URL}"
|
||||
|
||||
# `.xcconfig` treats `//` as a comment opener. Break the URL with a helper setting
|
||||
|
||||
@@ -552,9 +552,9 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
"gateway.push.apns":
|
||||
"APNs delivery settings for iOS devices paired to this gateway. Use relay settings for official/TestFlight builds that register through the external push relay.",
|
||||
"gateway.push.apns.relay":
|
||||
"External relay settings for relay-backed APNs sends. The gateway uses this relay for push.test, wake nudges, and reconnect wakes after a paired official iOS build publishes a relay-backed registration.",
|
||||
"External relay settings for relay-backed APNs sends. The gateway uses the hosted OpenClaw relay by default, or this custom relay for push.test, wake nudges, and reconnect wakes after a paired official iOS build publishes a relay-backed registration.",
|
||||
"gateway.push.apns.relay.baseUrl":
|
||||
"Base HTTPS URL for the external APNs relay service used by official/TestFlight iOS builds. Keep this aligned with the relay URL baked into the iOS build so registration and send traffic hit the same deployment.",
|
||||
"Optional custom base HTTPS URL for the external APNs relay service used by official/TestFlight iOS builds. Keep this aligned with the relay URL baked into the iOS build so registration and send traffic hit the same deployment.",
|
||||
"gateway.push.apns.relay.timeoutMs":
|
||||
"Timeout in milliseconds for relay send requests from the gateway to the APNs relay (default: 10000). Increase for slower relays or networks, or lower to fail wake attempts faster.",
|
||||
"gateway.http.endpoints.chatCompletions.enabled":
|
||||
|
||||
@@ -87,7 +87,7 @@ const FIELD_PLACEHOLDERS: Record<string, string> = {
|
||||
"gateway.controlUi.basePath": "/openclaw",
|
||||
"gateway.controlUi.root": "dist/control-ui",
|
||||
"gateway.controlUi.allowedOrigins": "https://control.example.com",
|
||||
"gateway.push.apns.relay.baseUrl": "https://relay.example.com",
|
||||
"gateway.push.apns.relay.baseUrl": "https://ios-push-relay.openclaw.ai",
|
||||
"channels.mattermost.baseUrl": "https://chat.example.com",
|
||||
"agents.list[].identity.avatar": "avatars/openclaw.png",
|
||||
};
|
||||
|
||||
@@ -5,7 +5,11 @@ import {
|
||||
publicKeyRawBase64UrlFromPem,
|
||||
verifyDeviceSignature,
|
||||
} from "./device-identity.js";
|
||||
import { resolveApnsRelayConfigFromEnv, sendApnsRelayPush } from "./push-apns.relay.js";
|
||||
import {
|
||||
DEFAULT_APNS_RELAY_BASE_URL,
|
||||
resolveApnsRelayConfigFromEnv,
|
||||
sendApnsRelayPush,
|
||||
} from "./push-apns.relay.js";
|
||||
|
||||
const relayGatewayIdentity = (() => {
|
||||
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
||||
@@ -60,11 +64,10 @@ function firstMockCall<T extends unknown[]>(mock: { mock: { calls: T[] } }): T |
|
||||
|
||||
describe("push-apns.relay", () => {
|
||||
describe("resolveApnsRelayConfigFromEnv", () => {
|
||||
it("returns a missing-config error when no relay base URL is configured", () => {
|
||||
expect(resolveApnsRelayConfigFromEnv({} as NodeJS.ProcessEnv)).toEqual({
|
||||
ok: false,
|
||||
error:
|
||||
"APNs relay config missing: set gateway.push.apns.relay.baseUrl or OPENCLAW_APNS_RELAY_BASE_URL",
|
||||
it("defaults to the hosted relay when no relay base URL is configured", () => {
|
||||
expectRelayConfig(resolveApnsRelayConfigFromEnv({} as NodeJS.ProcessEnv), {
|
||||
baseUrl: DEFAULT_APNS_RELAY_BASE_URL,
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ export type ApnsRelayRequestSender = (params: {
|
||||
payload: object;
|
||||
}) => Promise<ApnsRelayPushResponse>;
|
||||
|
||||
export const DEFAULT_APNS_RELAY_BASE_URL = "https://ios-push-relay.openclaw.ai";
|
||||
const DEFAULT_APNS_RELAY_TIMEOUT_MS = 10_000;
|
||||
const GATEWAY_DEVICE_ID_HEADER = "x-openclaw-gateway-device-id";
|
||||
const GATEWAY_SIGNATURE_HEADER = "x-openclaw-gateway-signature";
|
||||
@@ -113,17 +114,12 @@ export function resolveApnsRelayConfigFromEnv(
|
||||
const configuredRelay = gatewayConfig?.push?.apns?.relay;
|
||||
const envBaseUrl = normalizeNonEmptyString(env.OPENCLAW_APNS_RELAY_BASE_URL);
|
||||
const configBaseUrl = normalizeNonEmptyString(configuredRelay?.baseUrl);
|
||||
const baseUrl = envBaseUrl ?? configBaseUrl;
|
||||
const baseUrl = envBaseUrl ?? configBaseUrl ?? DEFAULT_APNS_RELAY_BASE_URL;
|
||||
const baseUrlSource = envBaseUrl
|
||||
? "OPENCLAW_APNS_RELAY_BASE_URL"
|
||||
: "gateway.push.apns.relay.baseUrl";
|
||||
if (!baseUrl) {
|
||||
return {
|
||||
ok: false,
|
||||
error:
|
||||
"APNs relay config missing: set gateway.push.apns.relay.baseUrl or OPENCLAW_APNS_RELAY_BASE_URL",
|
||||
};
|
||||
}
|
||||
: configBaseUrl
|
||||
? "gateway.push.apns.relay.baseUrl"
|
||||
: "default APNs relay base URL";
|
||||
|
||||
try {
|
||||
const parsed = new URL(baseUrl);
|
||||
|
||||
Reference in New Issue
Block a user