| … | … | @@ -0,0 +1,67 @@ |
| 1 | # atproto-identity |
| 2 | |
| 3 | **Seed-anchored, self-custody AT Protocol identity.** One BIP-39 mnemonic derives both the |
| 4 | Ed25519 key (Hypercore identity) and the secp256k1 key (`did:plc` signing) — so a user's |
| 5 | atproto DID is rooted in a seed they alone hold, not a hosting PDS. Includes offline |
| 6 | `did:plc` genesis + update building, did:key codec, session/service auth, device attestation, |
| 7 | and swarm discovery. |
| 8 | |
| 9 | Part of the **atproto-base** self-custody stack: |
| 10 | |
| 11 | ``` |
| 12 | atproto-repo serialization / codec |
| 13 | atproto-store P2P repo storage + firehose |
| 14 | atproto-identity ← you are here (identity + auth) |
| 15 | hyper-proto-peer the self-custody peer (multi-device swarm PDS) |
| 16 | atproto-bridge keyless membrane → Bluesky |
| 17 | ``` |
| 18 | |
| 19 | ## Modules |
| 20 | |
| 21 | - **Identity derivation** (`lib/atproto-identity.js`, main; `lib/binding.js`, |
| 22 | `lib/did-key.js`) — `deriveIdentity` (mnemonic → keys at `ATPROTO_SIGNING_PATH` / |
| 23 | `ATPROTO_RECOVERY_PATH`), `createAtprotoIdentity`, `deriveAtprotoKey`, `encodeDidKey` / |
| 24 | `decodeDidKey` (both curves, varint-multicodec). |
| 25 | - **did:plc ops** (`lib/did-plc.js`) — `buildGenesisOp`, `buildUpdateOp`, `encodeOp`, |
| 26 | `didForCreateOp` (DID = `did:plc:` + base32(sha256(signed genesis))[:24]), `opCid`, |
| 27 | `checkGenesisOp` / `checkUpdateOp`. Full-state ops, low-S signatures — byte-exact vs a |
| 28 | real registered DID. |
| 29 | - **PLC directory** (`lib/plc-directory.js`) — `PlcDirectory` client for the submit |
| 30 | ceremony (`GET /:did/log/last`, `POST /:did`). |
| 31 | - **Auth** (`lib/auth.js`) — `hashPassword` / `verifyPassword` (scrypt), `signJwt` / |
| 32 | `verifyJwt` (HS256 session tokens), and **`signAssertion` / `verifyAssertion`** (ES256K — |
| 33 | atproto's inter-service / `getServiceAuth` token shape). `parseBearer`, scopes. |
| 34 | - **Device attestation** (`lib/device-attestation.js`, `lib/device-chain.js`, |
| 35 | `lib/attested-anchor.js`) — attest additional device keys to one root identity |
| 36 | (`attestDeviceChain`, `buildAttestedAnchor`, `buildAttestedIdentity`) so N devices share |
| 37 | one DID. `rotateVerificationMethod` keeps the root's 72h PLC recovery authority. |
| 38 | - **Discovery** (`lib/discovery.js`) — `discoveryTopic(did)` for Hyperswarm rendezvous. |
| 39 | |
| 40 | ## Usage |
| 41 | |
| 42 | ```js |
| 43 | import { deriveIdentity, buildGenesisOp, didForCreateOp } from 'atproto-identity/lib/...' |
| 44 | import { signAssertion, verifyAssertion } from 'atproto-identity/lib/auth.js' |
| 45 | |
| 46 | const id = await deriveIdentity(mnemonic, { handle, pdsUrl }) |
| 47 | const genesis = buildGenesisOp({ ...id, handle, pds }) |
| 48 | const did = didForCreateOp(genesis) // did:plc:... |
| 49 | |
| 50 | // service-auth (the token hyper-proto-peer mints for the bridge's AppView proxy): |
| 51 | const jwt = signAssertion({ iss: did, aud: 'did:web:api.bsky.app', exp, lxm }, privKey) |
| 52 | ``` |
| 53 | |
| 54 | ## Design notes |
| 55 | |
| 56 | - **The offline half is proven** (genesis byte-exact vs a real `did:plc`; golden test). The |
| 57 | live submit ceremony (`POST /:did` head-chaining, tombstone, recovery) is being wired via |
| 58 | `PlcDirectory` — see the `did-plc` reference skill for the full op contract. |
| 59 | - **Self-custody, not custodial.** The peer holds its own rotation key and submits PLC ops |
| 60 | directly to the directory — it never delegates signing to a hosting PDS. |
| 61 | - Pure JS crypto (`@noble`), Bare-safe. |
| 62 | |
| 63 | ## Test |
| 64 | |
| 65 | ``` |
| 66 | bare test/all.js # or: npm test (incl. golden-did-plc byte-exactness) |
| 67 | ``` |