perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Honor repo write swap preconditions

alice 457a0272 ef1538ef

+33
+1
docs/TEST_AUDIT.md
··· 54 54 - Remote `did:web` DID docs, conservative `resolveIdentity` handle validation, and external handle adoption all need explicit coverage because small resolver-policy drifts turn into visible interop bugs quickly. 55 55 - Remote `did:plc` DID docs should resolve through the PLC directory defaults even when `plc_url` is not explicitly configured; gating that path on local config silently breaks federated identity lookups. 56 56 - `com.atproto.repo.getRecord` must honor `cid` when present, and `putRecord` / `deleteRecord` must actually enforce `swapRecord`; those negative edges are now covered directly. 57 + - `com.atproto.repo.createRecord` must not ignore `swapRecord`, and `swapCommit` mismatches need direct negative coverage on write paths instead of relying on indirect `applyWrites` behavior. 57 58 - `com.atproto.server.requestPasswordReset` and `com.atproto.server.deleteAccount` now follow the reference form-token flow, with focused regression coverage for missing-account and bearerless deletion semantics. 58 59 - `com.atproto.server.createAccount` with an explicit `did` must behave like an authenticated migration flow: require auth from that same DID, keep the existing DID document, and start the new account deactivated until activation catches the DID document up to the new PDS. 59 60 - `com.atproto.server.checkAccountStatus` must validate the stored DID document against the PDS service endpoint and signing key, and `com.atproto.repo.describeRepo` must derive `didDoc` / `handleIsCorrect` from that document instead of hardcoding success.
+6
lib/ATProto/PDS/API/Repo.pm
··· 48 48 collection => $body->{collection}, 49 49 rkey => $body->{rkey}, 50 50 value => $body->{record}, 51 + (exists $body->{swapRecord} 52 + ? ( 53 + swap_record_present => 1, 54 + swap_record => $body->{swapRecord}, 55 + ) 56 + : ()), 51 57 }, include_result => 1); 52 58 }); 53 59
+26
t/repo-api.t
··· 127 127 })->status_is(400) 128 128 ->json_is('/error' => 'InvalidRequest'); 129 129 130 + $t->post_ok('/xrpc/com.atproto.repo.createRecord' => { Authorization => "Bearer $access" } => json => { 131 + repo => $did, 132 + collection => 'app.bsky.feed.post', 133 + rkey => 'swap-create', 134 + swapRecord => 'bafyreifakecidmismatch', 135 + record => { 136 + '$type' => 'app.bsky.feed.post', 137 + text => 'create should reject swapRecord', 138 + createdAt => '2026-03-10T00:00:45Z', 139 + }, 140 + })->status_is(400) 141 + ->json_is('/error' => 'InvalidSwap'); 142 + 130 143 $t->post_ok('/xrpc/com.atproto.repo.putRecord' => { Authorization => "Bearer $access" } => json => { 131 144 repo => $did, 132 145 collection => 'app.bsky.feed.post', ··· 159 172 ->json_like('/cid' => qr/\Ab/) 160 173 ->json_has('/rev'); 161 174 my $pre_noop_commit = $t->tx->res->json; 175 + 176 + $t->post_ok('/xrpc/com.atproto.repo.putRecord' => { Authorization => "Bearer $access" } => json => { 177 + repo => $did, 178 + collection => 'app.bsky.feed.post', 179 + rkey => 'first-post', 180 + swapCommit => 'bafyreifakeheadmismatch', 181 + record => { 182 + '$type' => 'app.bsky.feed.post', 183 + text => 'swapCommit mismatch should fail', 184 + createdAt => '2026-03-10T00:02:15Z', 185 + }, 186 + })->status_is(400) 187 + ->json_is('/error' => 'InvalidSwap'); 162 188 163 189 $t->post_ok('/xrpc/com.atproto.repo.putRecord' => { Authorization => "Bearer $access" } => json => { 164 190 repo => $did,