// Slice 3: commit signing + full CAR origination, verified offline.
// - sign/verify round-trip + tamper detection (secp256k1)
// - createRepo -> re-read via slice-1 reader -> byte-exact, records intact
// - whole-stack compose: mnemonic (identity) -> did:plc -> sign a repo commit ->
// verify the sig against the DID's verificationMethod. No PLC/relay needed.
import test from 'brittle'
import * as dagCbor from '@ipld/dag-cbor'
import { secp256k1 } from '@noble/curves/secp256k1'
import { readCar, loadCommit, listRecords, verifyRepo, createRepo } from '../lib/repo.js'
import { unsignedCommit, signCommit, verifyCommit } from '../lib/commit.js'
import { cidForBytesSync } from '../lib/cid.js'
// cross-package (dev dep; offline; deriveAtprotoKey needs no native deps):
import { deriveAtprotoKey } from 'atproto-identity'
import { decodeDidKey } from 'atproto-identity/lib/did-key.js'
const MNEMONIC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
const DID = 'did:plc:yu4kgcktm7qtrwo5ofyiaf4c'
const RECORDS = [
{ collection: 'app.bsky.feed.post', rkey: '3aaa', record: { $type: 'app.bsky.feed.post', text: 'first', createdAt: '2026-07-01T00:00:00.000Z' } },
{ collection: 'app.bsky.feed.post', rkey: '3bbb', record: { $type: 'app.bsky.feed.post', text: 'second', createdAt: '2026-07-01T00:00:01.000Z' } },
{ collection: 'app.bsky.actor.profile', rkey: 'self', record: { $type: 'app.bsky.actor.profile', displayName: 'test' } }
]
// 1. sign / verify / tamper
const key = deriveAtprotoKey(MNEMONIC)
const dataCid = cidForBytesSync(dagCbor.encode('placeholder-mst-root')) // any valid CID for the sign/verify shape
test('sign / verify / tamper (secp256k1)', (t) => {
const signed = signCommit(unsignedCommit({ did: DID, rev: '3zzz', data: dataCid }), key.privateKey)
t.ok(signed.sig instanceof Uint8Array && signed.sig.length === 64 && verifyCommit(signed, key.publicKey),
'sign -> verify round-trips (secp256k1, 64-byte sig)')
const signed2 = signCommit(unsignedCommit({ did: DID, rev: '3zzz', data: dataCid }), key.privateKey)
const tampered = { ...signed2, rev: '3zzy' }
t.ok(!verifyCommit(tampered, key.publicKey), 'tampered commit fails verification')
})
// 2. full CAR origination -> re-read with the slice-1 reader
let repo
test('createRepo -> re-read via slice-1 reader (byte-exact, records intact)', async (t) => {
repo = await createRepo({ records: RECORDS, did: DID, rev: '3ccc', privateKey: key.privateKey })
const car = await readCar(repo.carBytes)
const v = await verifyRepo(car)
const commit = loadCommit(car)
t.comment(`originated CAR: ${repo.carBytes.length}b, ${v.blocks} blocks, ${v.records} records`)
t.ok(v.ok && commit.cid.equals(repo.commitCid) && commit.value.did === DID && commit.value.data.equals(repo.dataCid),
'createRepo produces a byte-exact, re-readable CAR')
const car2 = await readCar(repo.carBytes)
const recs = listRecords(car2)
const keys = recs.map(r => r.key).sort()
const want = ['app.bsky.actor.profile/self', 'app.bsky.feed.post/3aaa', 'app.bsky.feed.post/3bbb']
const post = recs.find(r => r.key === 'app.bsky.feed.post/3aaa')
t.ok(JSON.stringify(keys) === JSON.stringify(want) && post.value.text === 'first',
're-read records match what we put in (keys + text)')
})
// 3. whole-stack compose: identity did -> our commit -> verify sig vs the DID key
test('commit signed with mnemonic key verifies against the did:key', async (t) => {
const { pubkey, curve } = decodeDidKey(key.didKey)
const car = await readCar(repo.carBytes)
const signed = loadCommit(car).value
t.ok(curve === 'secp256k1' && verifyCommit(signed, pubkey),
'commit signed with mnemonic key verifies against the did:key')
})