From 3f32829e9c94851b0288d9331614446af18c30c0 Mon Sep 17 00:00:00 2001 From: Cody Robinson Date: Wed, 19 Aug 2026 21:59:03 -0400 Subject: [PATCH] Fix object store base64 encoding to keep url-safe padding nats.go encodes object names in meta subjects and object digests with padded url-safe base64 (base64.URLEncoding) and its digest decoder rejects unpadded input. We stripped the padding, so objects written by this client were unreadable by nats.go, and objects written by nats.go could not be looked up here whenever the encoded name required padding. Objects written by previous versions live under unpadded meta subjects and have to be re-put after this fix. --- CHANGELOG.md | 5 +++++ src/JetStream/ObjectStore/Store.php | 7 ++++++- tests/JetStream/ObjectStore/ObjectStoreTest.php | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35c1844..50f0b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed +* Keep base64url padding when encoding object store names and digests, matching the reference implementation (nats.go `base64.URLEncoding`). Objects written by previous versions use unpadded meta subjects and must be re-put to be visible to the fixed client and to other NATS clients. + ## [0.4.1] 2026-04-21 ### Added diff --git a/src/JetStream/ObjectStore/Store.php b/src/JetStream/ObjectStore/Store.php index abb8031..79944d4 100644 --- a/src/JetStream/ObjectStore/Store.php +++ b/src/JetStream/ObjectStore/Store.php @@ -327,8 +327,13 @@ public function seal(): void $this->js->updateStream($info->config->seal()); } + /** + * Padding must be kept: nats.go encodes object names in meta subjects and object + * digests with padded url-safe base64 (base64.URLEncoding) and rejects unpadded + * digests on read. + */ private function base64encode(string $name): string { - return rtrim(strtr(base64_encode($name), '+/', '-_'), '='); + return strtr(base64_encode($name), '+/', '-_'); } } diff --git a/tests/JetStream/ObjectStore/ObjectStoreTest.php b/tests/JetStream/ObjectStore/ObjectStoreTest.php index ab1620e..ac1023e 100644 --- a/tests/JetStream/ObjectStore/ObjectStoreTest.php +++ b/tests/JetStream/ObjectStore/ObjectStoreTest.php @@ -91,6 +91,12 @@ public function testPutObject(): void self::assertSame(10, $info->size); self::assertSame(1, $info->chunks); + // Padded url-safe base64, as written by nats.go (base64.URLEncoding). + self::assertSame( + 'SHA-256=' . strtr(base64_encode(hash('sha256', $body, true)), '+/', '-_'), + $info->digest, + ); + $object = $store->get('xfile'); self::assertSame($body, (string) $object);