# DID Onboarding Flow: Assessment & Implementation Plan

## Executive Summary

The current onboarding DID flow is **partially implemented** and has several significant gaps compared to the W3C DID protocol and Web5 expectations. The core `did:key` format is **correct**, but the user-facing flow includes mock/fake behavior and the backup/verify steps don't actually use the DID infrastructure.

---

## What We Have (Current State)

### ✅ Correct Implementation

| Component | Status | Notes |
|-----------|--------|-------|
| **did:key format** | ✅ Correct | Ed25519 multicodec `0xed 0x01`, base58btc encoding, `z` prefix |
| **Key generation** | ✅ Correct | Ed25519 via `ed25519_dalek`, persisted at `/var/lib/archipelago/identity/` |
| **node.did RPC** | ✅ Correct | Returns `{ did, pubkey }` from server state |
| **Identity persistence** | ✅ Correct | Key survives reboots, 0o600 permissions on Unix |
| **Sign/verify primitives** | ✅ Present | `NodeIdentity::sign()`, `NodeIdentity::verify()` exist in Rust |

### ⚠️ Partial / Misleading Implementation

| Component | Status | Issue |
|-----------|--------|-------|
| **OnboardingDid.vue** | ⚠️ Misleading copy | Says "Generate DID" but we *fetch* from server; key is created at first boot, not during onboarding |
| **OnboardingVerify.vue** | ❌ Fake | Uses `generateMockSignature()` – random chars, no backend call. Doesn't prove DID control |
| **OnboardingBackup.vue** | ❌ Non-functional | Backup is mock JSON with `{ did, kid }`; no encrypted key material; **restore is impossible** |
| **kid usage** | ⚠️ Non-standard | We store `pubkey` as `kid`; proper did:key uses fragment like `#key-1` or `did:key:z...#key-1` |

### ❌ Missing

| Component | Status |
|-----------|--------|
| **node.sign RPC** | Not exposed – backend can sign but no API |
| **Challenge-sign flow** | No backend support for proof-of-control |
| **Encrypted backup** | No real backup with key material or recovery path |
| **DID Document endpoint** | Not exposed (optional for did:key – can be derived client-side) |
| **keyAgreement / X25519** | Not derived – full DID Document would need Ed25519→X25519 for encryption |

---

## DID Protocol Requirements (W3C / Web5)

### did:key Method (W3C CCG)

1. **Format**: `did:key:z<base58btc(multicodec + raw-public-key-bytes)>` ✅ We do this
2. **DID Document**: Can be derived from the DID string; no registry. Libraries like `@digitalcredentials/did-method-key` expand it.
3. **Verification methods**: `verificationMethod`, `authentication`, `assertionMethod`, `keyAgreement` (X25519 derived), `capabilityDelegation`, `capabilityInvocation`
4. **Key ID (kid)**: Typically `{did}#key-1` or similar fragment

### Proof of Control

To prove control of a DID, you must **sign a challenge** with the private key. The verifier checks the signature against the public key in the DID. Our OnboardingVerify step claims to do this but **does not**.

### Backup / Recovery

A proper identity backup for recovery would:

- Include the private key (or encrypted key material)
- Be encrypted with a user passphrase
- Allow restore on a new device

Our backup has none of this – it's display-only.

---

## Recommended Implementation Plan

### Phase 1: Fix Verify Step (Proof of Control)

**Goal**: Replace the fake "Sign Challenge" with a real cryptographic proof.

1. **Backend**: Add `node.signChallenge` RPC
   - Input: `{ challenge: string }` (nonce from frontend)
   - Output: `{ signature: string }` (hex-encoded Ed25519 signature)
   - Uses `NodeIdentity::sign()` with `challenge.as_bytes()`

2. **Frontend (OnboardingVerify.vue)**:
   - Generate a random nonce (e.g. 32 bytes, base64)
   - Call `node.signChallenge({ challenge })`
   - Verify signature locally using the pubkey from `node.did` (optional – or trust server)
   - Display the real signature; remove `generateMockSignature()`

**Effort**: ~2–4 hours

---

### Phase 2: Improve UX and Terminology

**Goal**: Align copy and flow with actual behavior.

1. **OnboardingDid.vue**:
   - Change "Generate DID" → "Get your node's identity" or "Retrieve DID"
   - Clarify that the DID is created when the node first starts (not on button click)
   - Optionally auto-fetch on mount if identity exists (no button needed for returning state)

2. **kid / Key ID**:
   - Use `#key-1` or full `{did}#key-1` in backup and state
   - Or follow [did:key key IDs](https://www.w3.org/TR/did-core/#relative-did-urls)

**Effort**: ~1–2 hours

---

### Phase 3: Real Backup (Encrypted Export)

**Goal**: Backup that can actually be used for recovery.

**Design choice**: The private key lives on the **server**. Two options:

- **Option A (simpler)**: Backup is a signed, encrypted blob containing the key material. Restore requires:
  - Upload backup file
  - Enter passphrase
  - Server imports key and replaces current identity (or restores to same node)

- **Option B (more self-sovereign)**: User can export key to their own wallet. Higher complexity and key-handling risk.

**Recommended: Option A**

1. **Backend**: Add `node.createBackup` RPC
   - Input: `{ passphrase: string }`
   - Encrypt the raw key bytes (e.g. XChaCha20-Poly1305 or AES-256-GCM) with a key derived from passphrase (Argon2)
   - Return JSON: `{ version, did, backupBlob (base64), salt, ... }` or trigger download

2. **Backend**: Add `node.restoreBackup` RPC (for restore flow)
   - Input: `{ backupBlob, passphrase }`
   - Decrypt, validate, write to identity dir
   - Restart or reload identity

3. **Frontend (OnboardingBackup.vue)**:
   - Call `node.createBackup` instead of building mock JSON locally
   - Download the real backup file

4. **Restore flow**: Add a restore path (e.g. from login or onboarding options) that accepts backup file + passphrase and calls `node.restoreBackup`

**Effort**: ~1–2 days (crypto, testing, edge cases)

---

### Phase 4: DID Document & Web5 Interop (Optional)

**Goal**: Full compatibility with Web5 resolvers and DWN.

1. **DID Document endpoint**: `GET /.well-known/did.json` or `/did/{did}`
   - Resolve did:key to a full DID Document
   - Include `verificationMethod`, `authentication`, `keyAgreement` (X25519 from Ed25519)
   - Reference: [did:key expansion](https://github.com/digitalbazaar/did-method-key)

2. **X25519 derivation**: Add `curve25519-dalek` or equivalent; derive X25519 pubkey from Ed25519 for `keyAgreement`

3. **Web5/DWN**: Ensure `web5-dwn` and `did-wallet` use our node DID correctly for resolution and operations

**Effort**: ~2–3 days

---

### Phase 5: DID as Authentication (Future)

**Goal**: Use DID + proof instead of (or in addition to) password.

- DID Auth / SIOP flow: prove control of DID via challenge-response
- Could reduce or replace password for API access
- Larger design and security review required

**Effort**: TBD

---

## Priority Recommendation

| Priority | Phase | Reason |
|----------|-------|--------|
| **P0** | Phase 1 (Verify) | Removes fake crypto; proves DID control |
| **P1** | Phase 2 (UX) | Quick wins; honest representation of flow |
| **P2** | Phase 3 (Backup) | Makes backup/restore actually useful |
| **P3** | Phase 4 (DID Doc) | Needed for full Web5 interop |
| **P4** | Phase 5 (DID Auth) | Longer-term identity architecture |

---

## Quick Reference: Current vs. Target

| Step | Current | Target |
|------|---------|--------|
| DID fetch | `node.did` ✅ | Same, better UX |
| Prove control | Fake random "signature" ❌ | Real `node.signChallenge` |
| Backup | Mock JSON, no key ❌ | Encrypted key material + restore |
| kid | Raw pubkey | `#key-1` or standard fragment |
| Restore | Not possible | `node.restoreBackup` |
