chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// Produces artifacts with the reference implementation for pds.js to verify.
// Copied into the atproto worktree by scripts/space-reference/run.sh as a
// scratch file; never committed there.
import { mkdirSync, writeFileSync } from 'node:fs';
import { P256Keypair } from '@atproto/crypto';
import { cidForLex, encode } from '@atproto/lex-cbor';
import { parseCid } from '@atproto/lex-data';
import { it } from 'vitest';
import {
COMMIT_MAC_TYPE,
computeCommitMac,
computeRecordMac,
createSpaceSigHeaders,
createSpaceToken,
encapsulate,
formatMacHeader,
formatWantMacHeader,
RECORD_MAC_TYPE,
RepoCommit,
serializeRepo,
} from '../src/index.js';
const OUT = process.env.OUT_DIR ?? '/tmp/ref-space-artifacts';
const AUTHORITY = 'did:plc:refauthority';
const SPACE = `at://${AUTHORITY}/space/com.example.forum/general`;
const AUTHOR = 'did:plc:refuser';
const BLOB = 'bafkreigh2akiscaildcqabsyg3dfr6chu3fgpregiymsck7e7aqa4s52zy';
it('generates reference artifacts', async () => {
mkdirSync(OUT, { recursive: true });
const author = await P256Keypair.create({ exportable: true });
const authorKem = await P256Keypair.create({ exportable: true });
const authority = await P256Keypair.create({ exportable: true });
// In strict path order, which the archive requires. `com.e.p/x` sorts
// before `com.example.post/aaa` byte for byte.
const inputs = [
{ collection: 'com.e.p', rkey: 'x', record: { $type: 'com.e.p', n: 42 } },
{
collection: 'com.example.post',
rkey: 'aaa',
record: { $type: 'com.example.post', text: 'first' },
},
{
collection: 'com.example.post',
rkey: 'bbb',
record: { $type: 'com.example.post', text: 'second' },
},
{
collection: 'com.example.post',
rkey: 'ccc',
record: {
$type: 'com.example.post',
text: 'with a picture',
image: {
$type: 'blob',
ref: parseCid(BLOB),
mimeType: 'image/png',
size: 1234,
},
},
},
{
collection: 'com.example.reply',
rkey: 'zz',
record: { $type: 'com.example.reply', deep: { nested: [1, 2, 3] } },
},
];
const records = await Promise.all(
inputs.map(async (i) => ({
collection: i.collection as any,
rkey: i.rkey as any,
cid: await cidForLex(i.record as any),
bytes: encode(i.record as any),
})),
);
const rev = '3mrxrefrevaaa';
const commit = RepoCommit.fromRecords(records).toCommit({
space: SPACE,
repo: AUTHOR as any,
rev,
});
// As the repo host would answer one syncer's getRepo: a MAC under the
// secret of that syncer's key share. pds.js derives the secret itself from
// the KEM key and the key share.
const repoShare = await encapsulate(authorKem.did());
const commitMac = computeCommitMac(repoShare.sharedSecret, commit);
const chunks: Uint8Array[] = [];
for await (const chunk of serializeRepo(commit, records)) chunks.push(chunk);
writeFileSync(`${OUT}/repo.star`, Buffer.concat(chunks));
const recordShare = await encapsulate(authorKem.did());
const recordUri = `${SPACE}/${AUTHOR}/com.example.post/ccc`;
const recordMac = computeRecordMac(
recordShare.sharedSecret,
recordUri,
records[3].cid,
);
const delegationToken = await createSpaceToken(
'delegation',
{ iss: AUTHOR, sub: SPACE, aud: `${AUTHORITY}#atproto_pds` },
author,
);
// The key a holder signs with, which the credential names in `cnf.kid`.
const holder = await P256Keypair.create();
const credential = await createSpaceToken(
'credential',
{ iss: AUTHORITY, sub: SPACE, keyId: holder.did() as any },
authority,
);
const useSignature = await createSpaceSigHeaders(holder, {
authorization: `Atproto-Space-Credential ${credential}`,
audience: AUTHOR as any,
});
const b64 = (b: Uint8Array) => Buffer.from(b).toString('base64');
const hex = (b: Uint8Array) => Buffer.from(b).toString('hex');
writeFileSync(
`${OUT}/manifest.json`,
`${JSON.stringify(
{
producer: '@atproto/space',
space: SPACE,
author: AUTHOR,
authority: AUTHORITY,
userDidKey: author.did(),
authorityDidKey: authority.did(),
kemPrivateKeyHex: hex(await authorKem.export()),
kemDidKey: authorKem.did(),
rev,
recordCount: records.length,
commit: {
ver: commit.ver,
repo: commit.repo,
space: commit.space,
rev: commit.rev,
data: { $bytes: b64(commit.data) },
},
getRepo: {
wantMac: formatWantMacHeader(COMMIT_MAC_TYPE, repoShare.keyShare),
keyShare: repoShare.keyShare,
mac: formatMacHeader(COMMIT_MAC_TYPE, commitMac),
},
getRecord: {
wantMac: formatWantMacHeader(RECORD_MAC_TYPE, recordShare.keyShare),
keyShare: recordShare.keyShare,
record: {
uri: recordUri,
cid: records[3].cid.toString(),
value: {
...inputs[3].record,
image: { ...inputs[3].record.image, ref: { $link: BLOB } },
},
},
mac: formatMacHeader(RECORD_MAC_TYPE, recordMac),
},
delegationToken,
delegationAud: `${AUTHORITY}#atproto_pds`,
credential,
credentialKeyId: holder.did(),
useSignature,
expectedPaths: records.map((r) => `${r.collection}/${r.rkey}`),
},
null,
2,
)}\n`,
);
console.log(`wrote ${OUT}/repo.star and manifest.json`);
});