- have a pds and invite code ready (the bsky.social sharding code will break in exciting ways if you try to use it as the PDS)
- install openssl, jwt-cli, and goat
- generate a NIST P-256 keypair (K-256 is the default for atproto, but jwt-cli doesn't support it):
$ openssl ecparam -name prime256v1 -genkey -noout -out private.key
$ openssl pkcs8 -topk8 -nocrypt -in private.key -out private.pem
$ openssl ec -in private.pem -pubout -out public.pem
read EC key
writing EC key
- convert it to multibase encoding. can't find a cli tool that does this, so, uh:
$ npm i --silent @atproto/crypto
$ node -e 'console.log(require("@atproto/crypto").formatMultikey("ES256", process.argv[1]))' "$(openssl ec -pubin -in public.pem -text 2>/dev/null | grep -Pzo '(?s)(?<=pub:\n ).*(?=\nASN1)' | tr -d ':\n\0 ')"
zDnaeg9dgYJL3UUMHWcmNadNuEszJxjfBXJVWs2WsyMHjb89g
- make your did json at /.well-known/did.json:
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1",
"https://w3id.org/security/suites/secp256k1-2019/v1"
],
"id": "did:web:didweb.vriska.dev",
"alsoKnownAs": [
"at://vriska.dev"
],
"verificationMethod": [
{
"id": "did:web:didweb.vriska.dev#atproto",
"type": "Multikey",
"controller": "did:web:didweb.vriska.dev",
"publicKeyMultibase": "zDnaeg9dgYJL3UUMHWcmNadNuEszJxjfBXJVWs2WsyMHjb89g"
}
],
"service": [
{
"id": "#atproto_pds",
"type": "AtprotoPersonalDataServer",
"serviceEndpoint": "https://pds.vriska.dev"
}
]
}
- point your handle to the did (it can be the same domain but doesn't have to be)
- make sure it resolves properly
$ goat resolve vriska.dev | head -n2
{
"id": "did:web:didweb.vriska.dev",
- generate a service auth token to assert ownership of the did
$ ACCESS_TOKEN="$(jwt encode -A ES256 --exp=5m -i 'did:web:didweb.vriska.dev' -a 'did:web:pds.vriska.dev' -P "lxm=com.atproto.server.createAccount" -S @private.pem)"
- make the account
$ goat account create --pds-host https://pds.vriska.dev --handle vriska.dev --password "$PASSWORD" --invite-code "$INVITE_CODE" --email "$EMAIL" --existing-did 'did:web:didweb.vriska.dev' --service-auth "$ACCESS_TOKEN"
Success!
DID: did:web:didweb.vriska.dev
Handle: vriska.dev
- get the newly generated key
$ goat account login -u vriska.dev -p "$PASSWORD"
$ goat account plc recommended
{
"alsoKnownAs": [
"at://vriska.dev"
],
"verificationMethods": {
"atproto": "did:key:zQ3shjCZuawhGX55NqAAfCqTBaNfnPDLGRTKtpMvss1xa7Ayd"
},
"rotationKeys": [
"did:key:zQ3shqh6WnxZp5Tm3zNrwQVb68mAJA2Zqh3CH3cm1p8MzMsrR"
],
"services": {
"atproto_pds": {
"type": "AtprotoPersonalDataServer",
"endpoint": "https://pds.vriska.dev"
}
}
}
- update the did.json with it (just change the
publicKeyMultibase
field, don't include the did:key:
prefix)
- activate the account
$ goat account activate
- you should be able to sign in like normal
I had to add
-P "lxm=com.atproto.server.createAccount"
to step 8 :)