diff --git a/services/opencode/src/androidTest/kotlin/com/getcode/opencode/model/core/OpenCodePayloadTests.kt b/services/opencode/src/androidTest/kotlin/com/getcode/opencode/model/core/OpenCodePayloadTests.kt index 99eb964cc..e949832eb 100644 --- a/services/opencode/src/androidTest/kotlin/com/getcode/opencode/model/core/OpenCodePayloadTests.kt +++ b/services/opencode/src/androidTest/kotlin/com/getcode/opencode/model/core/OpenCodePayloadTests.kt @@ -60,7 +60,8 @@ class OpenCodePayloadTests { assertEquals(PayloadKind.Tip.value, encoded[0].toInt()) assertEquals(userId, encoded.subList(1, 17)) - // Trailing bytes reserved / zero. - assertEquals(listOf(0, 0, 0), encoded.subList(17, 20)) + // Trailing reserved bytes are filled with incrementing digits (1, 2, 3) rather than + // zeros, so the native scanner can't drop them as trailing padding. + assertEquals(listOf(1, 2, 3), encoded.subList(17, 20)) } } \ No newline at end of file diff --git a/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/OpenCodePayload.kt b/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/OpenCodePayload.kt index 7416e1d38..cb4604f24 100644 --- a/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/OpenCodePayload.kt +++ b/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/OpenCodePayload.kt @@ -85,10 +85,12 @@ data class OpenCodePayload( 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | T | User ID (16 bytes) | reserved (0) | + | T | User ID (16 bytes) | 1 | 2 | 3 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ (T) Type (1 byte) — PayloadKind.Tip (2). User ID (16 bytes) — the recipient's user id (a UUID) so others can tip them. Raw bytes, no rendezvous keypair. Matches the iOS TipCode.Payload frame. + Reserved (3 bytes) — filled with incrementing digits (1, 2, 3) instead of zeros so the + native scanner doesn't strip them as trailing zero padding. */ diff --git a/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/PayloadKind.kt b/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/PayloadKind.kt index 14725b9cd..94ee2c192 100644 --- a/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/PayloadKind.kt +++ b/services/opencode/src/main/kotlin/com/getcode/opencode/model/core/PayloadKind.kt @@ -70,8 +70,9 @@ sealed interface PayloadKind { /** * A profile "tip code": the recipient's [UserId] (a 16-byte UUID) written raw at offset 1, - * with the trailing bytes reserved. Carries no nonce and derives no rendezvous — matches the - * iOS `TipCode.Payload` frame. + * with the trailing reserved bytes filled with incrementing digits (1, 2, 3, ...) so the + * native scanner can't drop them as trailing zero padding. Carries no nonce and derives no + * rendezvous — matches the iOS `TipCode.Payload` frame. */ data object Tip : PayloadKind { override val value: Int = 2 @@ -84,6 +85,14 @@ sealed interface PayloadKind { userId.take(OpenCodePayload.USER_ID_LENGTH).forEachIndexed { index, byte -> data[index + OpenCodePayload.OFFSET_USER_ID] = byte } + + // Fill the reserved trailing space with incrementing digits (1, 2, 3, ...) rather + // than zeros, so `Scanner.decode` can't drop them as trailing zero padding and the + // frame round-trips at full length. + val reservedStart = OpenCodePayload.OFFSET_USER_ID + OpenCodePayload.USER_ID_LENGTH + for (index in reservedStart until OpenCodePayload.LENGTH) { + data[index] = (index - reservedStart + 1).toByte() + } return data }