diff --git a/eas.json b/eas.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/apn-relay/src/index.ts b/packages/apn-relay/src/index.ts index 41d516a311..29d610d4e6 100644 --- a/packages/apn-relay/src/index.ts +++ b/packages/apn-relay/src/index.ts @@ -264,6 +264,7 @@ app.post("/v1/device/register", async (c) => { token: tail(row.device_token), env: row.apns_env, bundle: row.bundle_id, + secretHash: `${key.slice(0, 12)}...`, }) await db @@ -293,18 +294,16 @@ app.post("/v1/device/unregister", async (c) => { ) } + const key = hash(check.data.secret) + console.log("[relay] unregister", { token: tail(check.data.deviceToken), + secretHash: `${key.slice(0, 12)}...`, }) await db .delete(device_registration) - .where( - and( - eq(device_registration.secret_hash, hash(check.data.secret)), - eq(device_registration.device_token, check.data.deviceToken), - ), - ) + .where(and(eq(device_registration.secret_hash, key), eq(device_registration.device_token, check.data.deviceToken))) return c.json({ ok: true }) }) @@ -327,9 +326,18 @@ app.post("/v1/event", async (c) => { console.log("[relay] event", { type: check.data.eventType, session: check.data.sessionID, + secretHash: `${key.slice(0, 12)}...`, devices: list.length, }) if (!list.length) { + const [total] = await db.select({ value: sql`count(*)` }).from(device_registration) + console.log("[relay] event:no-matching-devices", { + type: check.data.eventType, + session: check.data.sessionID, + secretHash: `${key.slice(0, 12)}...`, + totalDevices: Number(total?.value ?? 0), + }) + return c.json({ ok: true, sent: 0, diff --git a/packages/opencode/src/cli/cmd/serve.ts b/packages/opencode/src/cli/cmd/serve.ts index ddca13f181..b6dfba439c 100644 --- a/packages/opencode/src/cli/cmd/serve.ts +++ b/packages/opencode/src/cli/cmd/serve.ts @@ -76,6 +76,9 @@ export const ServeCommand = cmd({ const relaySecret = input || randomBytes(18).toString("base64url") if (!input) { console.log("experimental push relay secret generated") + console.log( + "set --relay-secret or OPENCODE_EXPERIMENTAL_PUSH_RELAY_SECRET to keep push registrations stable across server restarts", + ) } if (relayURL && relaySecret) { const host = server.hostname ?? opts.hostname diff --git a/packages/opencode/src/server/push-relay.ts b/packages/opencode/src/server/push-relay.ts index f612f78698..b41e576fbb 100644 --- a/packages/opencode/src/server/push-relay.ts +++ b/packages/opencode/src/server/push-relay.ts @@ -1,4 +1,5 @@ import os from "node:os" +import { createHash } from "node:crypto" import { SessionID } from "@/session/schema" import { GlobalBus } from "@/bus/global" import { Log } from "@/util/log" @@ -56,6 +57,11 @@ function norm(input: string) { return input.replace(/\/+$/, "") } +function secretHash(input: string) { + if (!input) return "none" + return `${createHash("sha256").update(input).digest("hex").slice(0, 12)}...` +} + /** * Classify an IPv4 address into a reachability tier. * Lower number = more likely reachable from an external/overlay network device. @@ -256,6 +262,7 @@ async function post(input: { type: Type; sessionID: string }) { console.log("[ APN RELAY ] posting event", { relayURL: next.relayURL, + secretHash: secretHash(next.relaySecret), type: input.type, sessionID: input.sessionID, title: content.title, @@ -263,6 +270,7 @@ async function post(input: { type: Type; sessionID: string }) { log.info("[ APN RELAY ] posting event", { relayURL: next.relayURL, + secretHash: secretHash(next.relaySecret), type: input.type, sessionID: input.sessionID, title: content.title, @@ -285,6 +293,7 @@ async function post(input: { type: Type; sessionID: string }) { if (res.ok) { console.log("[ APN RELAY ] relay accepted event", { status: res.status, + secretHash: secretHash(next.relaySecret), type: input.type, sessionID: input.sessionID, title: content.title, @@ -292,6 +301,7 @@ async function post(input: { type: Type; sessionID: string }) { log.info("[ APN RELAY ] relay accepted event", { status: res.status, + secretHash: secretHash(next.relaySecret), type: input.type, sessionID: input.sessionID, title: content.title,