diff --git a/docs/docs/Getting Started/getting-started.md b/docs/docs/Getting Started/getting-started.md
index 88f95714..e52a7284 100644
--- a/docs/docs/Getting Started/getting-started.md
+++ b/docs/docs/Getting Started/getting-started.md
@@ -2,46 +2,203 @@
sidebar_position: 1
---
-# Tutorial Intro
+# Getting Started with W3DS
-Let's discover **Docusaurus in less than 5 minutes**.
+Welcome to **W3DS (Web 3 Data Spaces)** - a decentralized data synchronization protocol that puts users in control of their data.
-## Getting Started
+## What is W3DS?
-Get started by **creating a new site**.
+W3DS is a protocol that enables seamless data synchronization across multiple platforms while ensuring users own and control their data. Instead of platforms storing user data in silos, W3DS allows users to store their data in their own **eVaults** and have platforms sync from these vaults.
-Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
+## Core Concept
-### What you'll need
+The fundamental principle of W3DS is simple: **Users, groups, and objects own their own eVaults**. All data about a person, group, or object is stored in their eVault, and platforms act as frontends that display and interact with this data, while also serving as caches and aggregators for improved performance and user experience.
-- [Node.js](https://nodejs.org/en/download/) version 20.0 or above:
- - When installing Node.js, you are recommended to check all checkboxes related to dependencies.
+### Key Principles
-## Generate a new site
+1. **Data Ownership & Decentralized Storage**: Users own their data, not platforms. Each user has their own eVault for data storage, ensuring true data ownership and control.
-Generate a new Docusaurus site using the **classic template**.
+2. **Platform Independence & Automatic Synchronization**: Platforms are interchangeable frontends that automatically synchronize data, while also serving as caches and aggregators. Data created on one platform automatically appears on all platforms, enabling true interoperability across the ecosystem.
-The classic template will automatically be added to your project after you run the command:
+## How It Works: A Simple Example
-```bash
-npm init docusaurus@latest my-website classic
+Imagine User A creates a post on **Blabsy** (a social media platform):
+
+1. User A posts "Hello, world!" on Blabsy
+2. Blabsy's Web3 Adapter syncs the post to User A's eVault
+3. User A's eVault stores the post and notifies all registered platforms
+4. **Pictique** (another social media platform) receives the notification
+5. Pictique creates the post locally - User A's post automatically appears on Pictique through the synchronization system
+
+This is the power of W3DS: your data follows you across all platforms automatically.
+
+## Architecture Overview
+
+```mermaid
+graph TB
+ subgraph Users["Users & Groups"]
+ UserA[User A
eName: @user-a.w3id]
+ UserB[User B
eName: @user-b.w3id]
+ Group1[Group 1
eName: @group-1.w3id]
+ end
+
+ subgraph EVaults["eVaults"]
+ EVaultA[User A's eVault]
+ EVaultB[User B's eVault]
+ EVaultG1[Group 1's eVault]
+ end
+
+ subgraph Platforms["Platforms"]
+ Blabsy[Blabsy]
+ Pictique[Pictique]
+ OtherPlatform[Other Platforms]
+ end
+
+ subgraph Infrastructure["Infrastructure"]
+ Registry[Registry Service
W3ID Resolution]
+ EVaultCore[eVault Core
GraphQL API]
+ end
+
+ UserA -->|Owns| EVaultA
+ UserB -->|Owns| EVaultB
+ Group1 -->|Owns| EVaultG1
+
+ Blabsy -->|Read/Write| EVaultA
+ Pictique -->|Read/Write| EVaultA
+ OtherPlatform -->|Read/Write| EVaultA
+
+ EVaultA -->|Webhooks| Blabsy
+ EVaultA -->|Webhooks| Pictique
+ EVaultA -->|Webhooks| OtherPlatform
+
+ Blabsy -.->|Resolve eName| Registry
+ Pictique -.->|Resolve eName| Registry
+ OtherPlatform -.->|Resolve eName| Registry
+ EVaultCore -->|Store Data| EVaultA
+
+ style UserA fill:#e1f5ff,color:#000000
+ style EVaultA fill:#fff4e1,color:#000000
+ style Blabsy fill:#e8f5e9,color:#000000
+ style Pictique fill:#e8f5e9,color:#000000
+ style Registry fill:#f3e5f5,color:#000000
+ style EVaultCore fill:#f3e5f5,color:#000000
```
-You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.
+## Key Components
+
+### eVault Core
-The command also installs all necessary dependencies you need to run Docusaurus.
+The **eVault Core** is the central storage system that manages user data. It provides:
-## Start your site
+- **GraphQL API** for storing and retrieving data using MetaEnvelope storage for structured data
+- **Webhook delivery** to notify platforms of data changes
+- **Access control** via ACLs (Access Control Lists)
-Run the development server:
+### Web3 Adapter
-```bash
-cd my-website
-npm run start
+The **Web3 Adapter** is a library that platforms use to:
+
+- Handle bidirectional data synchronization between local databases and eVaults
+- Convert between platform-specific schemas and global ontology schemas
+
+### Registry Service
+
+The **Registry Service** provides:
+
+- **W3ID resolution**: Maps eNames (like `@user-a.w3id`) to eVault URLs
+- **Key binding certificates**: Stores user public keys for signature verification (used when platforms verify user signatures during authentication)
+- **Platform registration**: Tracks active platforms for webhook delivery
+
+### Platforms
+
+**Platforms** are applications that:
+
+- Display and interact with user data
+- Act as caches and aggregators for improved performance
+- Sync data to/from user eVaults
+- Convert between local and global data schemas
+- Handle webhooks to receive data updates
+
+## Data Flow
+
+When a user creates data on a platform:
+
+```text
+User Action → Platform Database → Web3 Adapter → User's eVault → Webhooks → All Platforms
```
-The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.
+1. **User Action**: User creates a post, message, or other data
+2. **Platform Database**: Platform stores data locally
+3. **Web3 Adapter**: Adapter converts data to global schema and syncs to eVault
+4. **User's eVault**: eVault stores the data as a MetaEnvelope
+5. **Webhooks**: eVault sends webhooks to all registered platforms (except the originating one)
+6. **All Platforms**: Other platforms receive webhooks and create the data locally
+
+> **Note**: This is a simplified overview of the data flow. The current implementation uses a basic webhook delivery mechanism. For production deployments, platforms should implement message delivery queues to handle eVault and platform downtime gracefully, ensuring reliable data synchronization.
+
+### Detailed Data Flow Sequence
+
+The following sequence diagram shows the detailed interactions between components, including the Web3 Adapter's internal implementation:
+
+```mermaid
+sequenceDiagram
+ participant User as User
+ participant Platform as Platform
+ participant LocalDB as Platform Database
+ participant Adapter as Web3 Adapter
+ participant Registry as Registry Service
+ participant EVault as User's eVault
+ participant OtherPlatforms as Other Platforms
+
+ User->>Platform: Create post
+ Platform->>LocalDB: Store post locally
+ Platform->>Adapter: Trigger sync
+ Adapter->>Adapter: Convert to global schema
(ontology mapping)
+ Adapter->>Registry: Resolve eName to eVault URL
+ Registry-->>Adapter: Return eVault URL
+ Adapter->>EVault: POST GraphQL mutation
(storeMetaEnvelope)
+ EVault->>EVault: Store MetaEnvelope
+ EVault-->>Adapter: Return stored MetaEnvelope
+ Adapter-->>Platform: Sync complete
+ EVault->>EVault: Wait 3 seconds
(prevent ping-pong)
+ EVault->>Registry: Get active platforms
+ Registry-->>EVault: Return platform list
+ EVault->>OtherPlatforms: POST webhook
(data change notification)
+ OtherPlatforms->>OtherPlatforms: Process webhook
(create post locally)
+```
+
+### Registration Sequence
+
+The following sequence diagram shows how a new user registers and creates their eVault, illustrating the roles of the Provisioner and Registry services:
+
+```mermaid
+sequenceDiagram
+ participant User as User
+ participant Wallet as eID Wallet
+ participant Provisioner as Provisioner Service
+ participant Registry as Registry Service
+ participant EVault as eVault Core
+
+ User->>Wallet: Initiate onboarding
+ Wallet->>Wallet: Generate hardware keys
(ECDSA P-256)
+ Wallet->>Provisioner: POST /provision
(eName, publicKey)
+ Provisioner->>EVault: Create eVault instance
+ EVault-->>Provisioner: eVault URL
+ Provisioner->>Registry: Register eName → eVault URL
+ Registry->>Registry: Store W3ID mapping
+ Registry->>Registry: Issue key binding certificate
(JWT with public key)
+ Registry-->>Provisioner: Registration complete
+ Provisioner->>EVault: Store public key
(for signature verification)
+ EVault-->>Provisioner: Public key stored
+ Provisioner-->>Wallet: eVault URL + certificate
+ Wallet->>Wallet: Store eVault URL
+ Wallet-->>User: Onboarding complete
+```
-The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.
+## Next Steps
-Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.
+- Learn more about [W3DS Basics](/docs/W3DS%20Basics/getting-started) - Deep dive into eVault ownership and data flow
+- Understand [Authentication](/docs/W3DS%20Protocol/Authentication) - How users authenticate with platforms
+- Learn about [Signing](/docs/W3DS%20Protocol/Signing) - Signature creation and verification
+- Explore [Signature Formats](/docs/W3DS%20Protocol/Signature-Formats) - Technical details on cryptographic signatures
+- Build a platform with the [Post Platform Guide](/docs/Post%20Platform%20Guide/getting-started) - Step-by-step guide to creating a W3DS-compatible platform
diff --git a/docs/docs/Infrastructure/eID-Wallet.md b/docs/docs/Infrastructure/eID-Wallet.md
new file mode 100644
index 00000000..8d4dad71
--- /dev/null
+++ b/docs/docs/Infrastructure/eID-Wallet.md
@@ -0,0 +1,410 @@
+---
+sidebar_position: 2
+---
+
+# eID Wallet
+
+The eID Wallet is a mobile application that manages cryptographic keys, authenticates users with platforms, and provides the user interface for eVault creation and management.
+
+## Overview
+
+The eID Wallet is a **Tauri-based mobile application** (built with SvelteKit and TypeScript) that serves as the primary interface for users in the W3DS ecosystem. It uses secure cryptographic enclaves on modern mobile devices to store and manage private keys without exposing them.
+
+### Key Features
+
+- **Key Management**: Generate and manage ECDSA P-256 key pairs
+- **Hardware Security**: Uses device cryptographic enclaves (Secure Enclave on iOS, Hardware Security Module on Android)
+- **eVault Creation**: User interface for provisioning a new eVault (one eVault per user)
+- **Platform Authentication**: Sign session IDs for platform login
+- **Signature Creation**: Sign arbitrary payloads for various use cases
+- **Key Rotation**: Planned feature for rotating keys in case of security incidents (not yet implemented)
+- **Multi-Device Support**: Keys can be synced across devices. See [eVault Key Delegation](/docs/Infrastructure/eVault-Key-Delegation) for details on how multiple devices link to the same eName.
+
+## Architecture
+
+```mermaid
+graph TB
+ subgraph Wallet["eID Wallet App"]
+ UI[SvelteKit UI]
+ KeyService[Key Service]
+ VaultController[Vault Controller]
+ AuthController[Auth Controller]
+ end
+
+ subgraph KeyManagers["Key Managers"]
+ Hardware[HW Key Manager
Secure Enclave/HSM]
+ Software[SW Key Manager
Web Crypto API]
+ end
+
+ subgraph External["External Services"]
+ Platform[Platform API
Authentication]
+ EVault[eVault Core
Key Storage]
+ Registry[Registry Service
W3ID Resolution]
+ end
+
+ UI -->|User Actions| KeyService
+ UI -->|Vault Operations| VaultController
+ UI -->|Auth Requests| AuthController
+
+ KeyService -->|Get Manager| Hardware
+ KeyService -->|Get Manager| Software
+
+ Hardware -->|Native APIs| Device[Device Hardware]
+ Software -->|Web Crypto| Browser[Browser APIs]
+
+ AuthController -->|Sign Session| KeyService
+ AuthController -->|POST /api/auth/login| Platform
+
+ VaultController -->|Provision eVault| EVault
+ VaultController -->|Resolve W3ID| Registry
+ KeyService -->|Sync Public Key| EVault
+
+ style KeyService fill:#e1f5ff,color:#000000
+ style Hardware fill:#fff4e1,color:#000000
+ style Software fill:#e8f5e9,color:#000000
+ style EVault fill:#f3e5f5,color:#000000
+```
+
+## Key Management
+
+The eID Wallet manages cryptographic keys through a flexible key manager system.
+
+### Key Manager Types
+
+#### Hardware Key Manager
+
+Uses device-native cryptographic APIs:
+
+- **iOS**: Secure Enclave via LocalAuthentication framework
+- **Android**: Hardware Security Module (HSM) via KeyStore API
+- **Benefits**: Private keys never leave the secure hardware
+- **Limitations**: Device-specific, cannot export keys
+
+#### Software Key Manager
+
+Uses Web Crypto API:
+
+- **Storage**: Keys stored in browser's secure storage
+- **Benefits**: Works on all platforms, can be exported
+- **Limitations**: Less secure than hardware keys
+
+### Key Manager Selection
+
+The wallet automatically selects the appropriate key manager:
+
+1. **Pre-verification Mode**: Always uses software keys (for testing/fake users only)
+2. **Real KYC/Verification**: Always uses hardware keys (never software keys)
+3. **Hardware Available**: Requires hardware keys; onboarding is blocked if hardware keys are unavailable (no software fallback)
+4. **Explicit Request**: Can force hardware or software based on configuration (for non-onboarding operations)
+
+## Key Operations
+
+### Generate Key
+
+Generate a new ECDSA P-256 key pair:
+
+**Process**:
+1. Determine key manager (hardware or software)
+2. Generate key pair using appropriate API
+3. Store key identifier (not the private key itself)
+4. Return public key
+
+**Implementation**:
+- Hardware: Uses device-native key generation
+- Software: Uses `crypto.subtle.generateKey()` with ECDSA P-256 parameters
+
+### Get Public Key
+
+Retrieve the public key for a given key ID:
+
+**Process**:
+1. Load key manager for the key ID
+2. Extract public key from key pair
+3. Encode as multibase format
+4. Return public key string
+
+**Format**: Multibase-encoded (starts with 'z' for base58btc or 'm' for base64)
+
+### Sign Payload
+
+Sign a string payload with a private key:
+
+**Process**:
+1. Convert payload string to UTF-8 bytes
+2. Compute SHA-256 hash
+3. Sign hash with ECDSA P-256
+4. Encode signature (base64 for software, multibase for hardware)
+5. Return signature string
+
+**Algorithm**: ECDSA P-256 with SHA-256
+
+**Signature Format**:
+- Software keys: Base64-encoded 64-byte raw signature
+- Hardware keys: Multibase base58btc-encoded signature
+
+### Verify Signature
+
+Verify a signature against a payload (for testing):
+
+**Process**:
+1. Get public key for key ID
+2. Decode signature
+3. Hash payload with SHA-256
+4. Verify signature using ECDSA P-256
+5. Return boolean result
+
+## User Journeys
+
+### Onboarding and eVault Creation
+
+When a new user first opens the wallet:
+
+1. **Generate Keys**: Create default key pair (hardware keys for real users, software keys only for pre-verification/test users)
+2. **Request Entropy**: Get entropy token from Registry
+3. **Generate Namespace**: Create UUID for namespace
+4. **Provision eVault**: Send provision request with public key to the Provisioner service (not to eVault Core, as no eVault exists yet, and not to Registry)
+5. **Receive Credentials**: Get W3ID (eName) and eVault URI
+6. **Store Locally**: Save credentials in wallet storage
+
+**API Flow**:
+```
+Wallet → Registry: GET /entropy
+Registry → Wallet: JWT entropy token
+Wallet → Provisioner: POST /provision (entropy, namespace, publicKey)
+Provisioner → Registry: Request key binding certificate
+Registry → Provisioner: JWT certificate
+Provisioner → Wallet: w3id, evaultUri
+```
+
+**Note**: The `/provision` endpoint is part of the Provisioner service, not eVault Core. This is the **provisioning protocol** - any vault provider should expose such an endpoint to enable eVault creation.
+
+### Platform Authentication
+
+User authenticating their eName to a platform:
+
+When a user wants to log into a platform:
+
+1. **Scan QR Code**: Platform displays QR code with `w3ds://auth` URI in the format:
+ ```text
+ w3ds://auth?redirect={url}&session={sessionId}&platform={platformName}
+ ```
+2. **Parse URI**: Extract `session` (session ID) and `redirect` (redirect URL) from the URI query parameters
+3. **Sign Session**: Sign session ID with default key
+4. **Send to Platform**: POST signed session to platform's `/api/auth/login` endpoint (the `redirect` URL from step 2)
+5. **Receive Token**: Platform verifies signature and returns auth token
+
+For detailed information on:
+- **Signature verification flow**: See [Signing documentation](/docs/W3DS%20Protocol/Signing) for the complete verification process (including Registry resolution, eVault `/whois` endpoint, and JWT certificate verification)
+- **Auth token generation**: See [Authentication documentation](/docs/W3DS%20Protocol/Authentication) for how platforms generate authentication tokens after signature verification
+
+**Signing Details**:
+- Uses key ID `"default"`
+- Uses context `"onboarding"` for real users (always hardware keys) or `"pre-verification"` for fake/test users (software keys)
+- For real KYC-verified users: Always uses hardware keys, never software keys
+- Signs the exact session ID string (UUID)
+- Returns base64 or multibase-encoded signature
+
+### Key Rotation (Conceptual - Not Yet Implemented)
+
+Key rotation is a planned feature that would allow users to rotate their keys in case of security incidents or device loss. The concept would involve:
+
+1. **Generate New Key**: Create new key pair
+2. **Sync to eVault**: Send new public key to eVault
+3. **Update Certificates**: eVault requests new key binding certificate
+4. **Revoke Old Key**: Optionally revoke old key (if supported)
+
+**Note**: The W3ID (eName) would remain the same - only the keys would change. This feature is on the roadmap but not currently implemented.
+
+## Public Key Syncing
+
+Public keys must be synced to eVault so platforms can verify signatures.
+
+### Sync Process
+
+1. **Get Public Key**: Retrieve public key from key manager
+2. **Format**: Ensure public key is in multibase format
+3. **Send to eVault**: POST to eVault's key storage endpoint
+4. **Certificate Generation**: eVault requests key binding certificate from Registry
+5. **Storage**: Certificate stored in eVault for future verification
+
+### Sync Timing
+
+- **During Provisioning**: Public key included in `/provision` request
+- **Multi-Device**: Each device syncs its own public key (see [eVault Key Delegation](/docs/Infrastructure/eVault-Key-Delegation) for details)
+
+## Signature Creation
+
+The wallet creates signatures for various purposes:
+
+### Authentication Signatures
+
+**Purpose**: Prove identity to platforms
+
+**Payload**: Session ID (UUID string)
+
+**Process**:
+1. Platform generates session ID
+2. Wallet receives session ID via `w3ds://auth` URI
+3. Wallet signs session ID with default key
+4. Wallet sends signature to platform
+
+### Document Signatures
+
+**Purpose**: Sign documents, contracts, or other data
+
+**Payload**: Arbitrary string (document hash, JSON, etc.)
+
+**Process**:
+1. User initiates signing action
+2. Wallet receives payload to sign
+3. Wallet signs payload with appropriate key
+4. Wallet returns signature to application
+
+### Voting Signatures
+
+**Purpose**: Sign votes in voting systems
+
+**Payload**: Vote session ID or vote data
+
+**Process**: Similar to document signatures, but with vote-specific payloads
+
+## Security Considerations
+
+### Private Key Protection
+
+- **Hardware Keys**: Private keys never leave secure hardware
+- **Software Keys**: Stored in browser's secure storage (encrypted at rest)
+- **No Export**: Private keys cannot be exported (security requirement)
+- **Biometric Protection**: Hardware keys require biometric authentication
+
+
+### Multi-Device Support
+
+- **Per-Device Keys**: Each device has its own key pair
+- **Multiple Certificates**: eVault can store multiple key binding certificates
+- **Verification**: Platforms try all certificates until one succeeds
+
+## Implementation Details
+
+### Technology Stack
+
+- **Framework**: Tauri (Rust + Web frontend)
+- **Frontend**: SvelteKit + TypeScript
+- **Key APIs**:
+ - iOS: LocalAuthentication (Secure Enclave)
+ - Android: KeyStore (HSM)
+ - Web: Web Crypto API
+
+### Key Service Architecture
+
+```typescript
+KeyService
+ ├── KeyManagerFactory
+ │ ├── HardwareKeyManager
+ │ └── SoftwareKeyManager
+ ├── Key Storage (encrypted)
+ └── Context Management
+```
+
+### Key Manager Interface
+
+All key managers implement:
+
+```typescript
+interface KeyManager {
+ exists(keyId: string): Promise;
+ generate(keyId: string): Promise;
+ getPublicKey(keyId: string): Promise;
+ signPayload(keyId: string, payload: string): Promise;
+ verifySignature(keyId: string, payload: string, signature: string): Promise;
+ getType(): "hardware" | "software";
+}
+```
+
+## API Integration
+
+### eVault Provisioning
+
+```typescript
+// Request entropy from Registry
+const entropyResponse = await fetch(`${registryUrl}/entropy`);
+const entropyToken = await entropyResponse.json();
+
+// Generate namespace
+const namespace = uuidv4();
+
+// Provision eVault with public key
+// Note: Real users always use hardware keys (context: "onboarding")
+// Pre-verification/test users use software keys (context: "pre-verification")
+const publicKey = await keyService.getPublicKey("default", "onboarding");
+const provisionResponse = await fetch(`${provisionerUrl}/provision`, {
+ method: "POST",
+ body: JSON.stringify({
+ registryEntropy: entropyToken,
+ namespace: namespace,
+ verificationId: verificationCode,
+ publicKey: publicKey
+ })
+});
+
+const { w3id, uri } = await provisionResponse.json();
+```
+
+**Note**: The `/provision` endpoint is hosted by the Provisioner service, not eVault Core.
+
+### Platform Authentication
+
+```typescript
+// Parse w3ds://auth URI
+const uri = new URL(authUri);
+const sessionId = uri.searchParams.get("session");
+const redirectUrl = uri.searchParams.get("redirect");
+
+// Sign session ID
+const signature = await keyService.signPayload(
+ "default",
+ "onboarding",
+ sessionId
+);
+
+// Send to platform
+const response = await fetch(redirectUrl, {
+ method: "POST",
+ body: JSON.stringify({
+ w3id: vault.ename,
+ session: sessionId,
+ signature: signature,
+ appVersion: "0.4.0"
+ })
+});
+
+const { token } = await response.json();
+```
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Hardware keys not available**
+ - Check device support (iOS 9+, Android 6+)
+ - Verify biometric authentication is set up
+ - **Note**: Real KYC-verified users must use hardware keys. Software keys are only for pre-verification/test users
+ - **Onboarding requirement**: The eID Wallet enforces hardware key manager during onboarding. Onboarding cannot proceed without hardware keys - there is no fallback to software keys for real users
+
+2. **Signature verification fails**
+ - Ensure using correct key ID and context
+ - Verify public key was synced to eVault
+ - Check signature encoding format
+
+3. **Key generation fails**
+ - Check device storage space
+ - Verify cryptographic APIs are available
+ - For real users: Hardware key generation must succeed (software fallback not acceptable)
+ - For pre-verification/test users: Software key manager can be used as fallback
+
+## References
+
+- [Authentication](/docs/W3DS%20Protocol/Authentication) - How wallet authentication works
+- [Signing](/docs/W3DS%20Protocol/Signing) - Signature creation details
+- [Signature Formats](/docs/W3DS%20Protocol/Signature-Formats) - Technical signature format details
+- [eVault](/docs/Infrastructure/eVault) - Where public keys are stored
diff --git a/docs/docs/Infrastructure/eVault-Key-Delegation.md b/docs/docs/Infrastructure/eVault-Key-Delegation.md
index 5940a21a..b7feb9bc 100644
--- a/docs/docs/Infrastructure/eVault-Key-Delegation.md
+++ b/docs/docs/Infrastructure/eVault-Key-Delegation.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 1
+sidebar_position: 3
---
# eVault Key Delegation
diff --git a/docs/docs/Infrastructure/eVault.md b/docs/docs/Infrastructure/eVault.md
new file mode 100644
index 00000000..e0ba9de8
--- /dev/null
+++ b/docs/docs/Infrastructure/eVault.md
@@ -0,0 +1,427 @@
+---
+sidebar_position: 1
+---
+
+# eVault
+
+eVault is the core storage system for W3DS. It provides a GraphQL API for storing and retrieving user data, manages access control, and delivers webhooks to platforms when data changes.
+
+## Overview
+
+An **eVault** is a personal data store identified by a **W3ID**. Each user, group, or object has their own eVault where all their data is stored in a standardized format called **MetaEnvelopes**.
+
+### Key Features
+
+- **GraphQL API**: Store, retrieve, update, and search data
+- **Access Control**: ACL-based permissions for data access
+- **Webhook Delivery**: Automatic notifications to platforms when data changes
+- **Key Binding**: Stores user public keys (generated in the eID wallet) for signature verification
+
+## Architecture
+
+eVault Core consists of several components:
+
+```mermaid
+graph TB
+ subgraph Client["Clients"]
+ Platform[Platforms]
+ Wallet[eID Wallet]
+ end
+
+ subgraph EVaultCore["eVault Core"]
+ GraphQL[GraphQL Server
/graphql]
+ HTTP[HTTP Server
/whois]
+ Webhook[Webhook Delivery]
+ AccessGuard[Access Guard
ACL Enforcement]
+ end
+
+ subgraph Storage["Storage"]
+ Neo4j[(Neo4j Database
MetaEnvelopes & Envelopes)]
+ end
+
+ subgraph External["External Services"]
+ Registry[Registry Service
W3ID Resolution]
+ end
+
+ Platform -->|GraphQL Mutations/Queries| GraphQL
+ Wallet -->|HTTP Requests| HTTP
+ GraphQL -->|Enforce ACLs| AccessGuard
+ GraphQL -->|Store/Query| Neo4j
+ GraphQL -->|Deliver Webhooks| Webhook
+ Webhook -->|Notify| Platform
+ AccessGuard -->|Resolve eName| Registry
+ HTTP -->|Store Data| Neo4j
+
+ style GraphQL fill:#e1f5ff,color:#000000
+ style Neo4j fill:#fff4e1,color:#000000
+ style Webhook fill:#e8f5e9,color:#000000
+```
+
+## Data Model
+
+### MetaEnvelopes
+
+A **MetaEnvelope** is the top-level container for an entity (post, user, message, etc.). It contains:
+
+- **id**: Unique identifier (UUID). Note: Only IDs registered in the Registry are guaranteed to be globally unique.
+- **ontology**: Schema identifier (UUID, e.g., "550e8400-e29b-41d4-a716-446655440001"). Ontology UUIDs can be resolved to their schema definitions via the ontology service. See [W3DS Basics](/docs/W3DS%20Basics/getting-started) for more information on ontology schemas.
+- **acl**: Access Control List (who can access this data)
+- **envelopes**: Array of individual Envelope nodes
+
+### Envelopes
+
+Each field in a MetaEnvelope becomes a separate **Envelope** node in Neo4j:
+
+- **id**: Unique identifier
+- **ontology**: The field name from the ontology schema (e.g., "content", "authorId", "createdAt") - this identifies which field in the schema this envelope represents
+- **value**: The actual field value (string, number, object, array)
+- **valueType**: Type of the value ("string", "number", "object", "array") - cached from the ontology schema for optimization purposes
+
+### Storage Structure
+
+In Neo4j, the structure looks like:
+
+```cypher
+(MetaEnvelope {id, ontology, acl}) -[:LINKS_TO]-> (Envelope {id, value, valueType})
+```
+
+This flat graph structure allows:
+- Efficient field-level updates
+- Flexible querying
+- Easy reconstruction of the original object
+
+**Trade-offs**:
+- Increased storage overhead (each field becomes a separate node)
+- More complex queries when reconstructing full objects
+- Potential performance impact with deeply nested structures
+
+## GraphQL API
+
+eVault exposes a GraphQL API at `/graphql` for all data operations.
+
+### Queries
+
+#### getMetaEnvelopeById
+
+Retrieve a specific MetaEnvelope by its global ID.
+
+**Query**:
+```graphql
+query {
+ getMetaEnvelopeById(id: "global-id-123") {
+ id
+ ontology
+ parsed
+ envelopes {
+ id
+ ontology
+ value
+ valueType
+ }
+ }
+}
+```
+
+**Headers Required**:
+- `X-ENAME`: The W3ID of the eVault owner
+- `Authorization: Bearer `: Platform authentication token
+
+#### findMetaEnvelopesByOntology
+
+Find all MetaEnvelopes of a specific ontology type.
+
+**Query**:
+```graphql
+query {
+ findMetaEnvelopesByOntology(ontology: "550e8400-e29b-41d4-a716-446655440001") {
+ id
+ ontology
+ parsed
+ }
+}
+```
+
+#### searchMetaEnvelopes
+
+Search MetaEnvelopes by content within a specific ontology. The `term` parameter performs case-sensitive text matching against the string values in any of the envelopes within MetaEnvelopes of the specified ontology. The search looks for the term within envelope values (not field names).
+
+**Query**:
+```graphql
+query {
+ searchMetaEnvelopes(ontology: "550e8400-e29b-41d4-a716-446655440001", term: "hello") {
+ id
+ ontology
+ parsed
+ }
+}
+```
+
+### Mutations
+
+#### storeMetaEnvelope
+
+Store a new MetaEnvelope in the eVault.
+
+**Mutation**:
+```graphql
+mutation {
+ storeMetaEnvelope(input: {
+ ontology: "550e8400-e29b-41d4-a716-446655440001"
+ payload: {
+ content: "Hello, world!"
+ mediaUrls: []
+ authorId: "..."
+ createdAt: "2025-01-24T10:00:00Z"
+ }
+ acl: ["*"]
+ }) {
+ metaEnvelope {
+ id
+ ontology
+ parsed
+ }
+ envelopes {
+ id
+ value
+ valueType
+ }
+ }
+}
+```
+
+**Headers Required**:
+- `X-ENAME`: The W3ID of the eVault owner (required)
+- `Authorization: Bearer `: Optional, but recommended for webhook delivery
+
+**Response Structure**:
+
+The `envelopes` array in the response contains one envelope per field in the payload, where each envelope's `ontology` field contains the field name from the schema. For example, storing a post with `content`, `authorId`, and `createdAt` fields produces:
+
+```json
+{
+ "envelopes": [
+ {
+ "ontology": "content",
+ "value": "Hello, world!"
+ },
+ {
+ "ontology": "authorId",
+ "value": "..."
+ },
+ {
+ "ontology": "createdAt",
+ "value": "2025-01-24T10:00:00Z"
+ },
+ {
+ "ontology": "mediaUrls",
+ "value": []
+ }
+ ],
+ "id": "9a84e965-2604-52bf-97a7-5c4f4151fea2"
+}
+```
+
+#### updateMetaEnvelopeById
+
+Update an existing MetaEnvelope.
+
+**Mutation**:
+```graphql
+mutation {
+ updateMetaEnvelopeById(
+ id: "global-id-123"
+ input: {
+ ontology: "550e8400-e29b-41d4-a716-446655440001"
+ payload: {
+ content: "Updated content"
+ mediaUrls: []
+ }
+ acl: ["*"]
+ }
+ ) {
+ metaEnvelope {
+ id
+ ontology
+ parsed
+ }
+ }
+}
+```
+
+#### deleteMetaEnvelope
+
+Delete a MetaEnvelope and all its Envelopes.
+
+**Mutation**:
+```graphql
+mutation {
+ deleteMetaEnvelope(id: "global-id-123")
+}
+```
+
+## HTTP API
+
+### /whois
+
+Get information about a W3ID, including key binding certificates.
+
+**Request**:
+```http
+GET /whois HTTP/1.1
+Host: evault.example.com
+X-ENAME: @user-a.w3id
+```
+
+**Response**:
+```json
+{
+ "keyBindingCertificates": [
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
+ ]
+}
+```
+
+**Use Case**: Platforms use this endpoint to retrieve public keys for signature verification.
+
+
+## Access Control
+
+eVault uses **Access Control Lists (ACLs)** to determine who can access data.
+
+### ACL Format
+
+ACLs are arrays of W3IDs or special values:
+
+- `["*"]`: Public read access (anyone can read, but only the eVault owner can write)
+- `["@user-a.w3id"]`: Only User A can access (read and write)
+- `["@user-a.w3id", "@user-b.w3id"]`: User A and User B can access (read and write)
+
+**Prototype Limitation**: In the current prototype implementation, ACLs provide all-or-nothing access. There is no read-only access without write access (except for `["*"]` which provides read-only access for everyone). More granular permissions are planned for future versions.
+
+### Access Enforcement
+
+The Access Guard middleware enforces ACLs:
+
+1. **Extract W3ID**: From `X-ENAME` header or Bearer token
+2. **Check ACL**: Verify the requesting W3ID is in the MetaEnvelope's ACL
+3. **Filter Results**: Remove ACL field from responses (security)
+4. **Allow/Deny**: Grant or deny access based on ACL
+
+### Special Cases
+
+- **storeMetaEnvelope**: Only requires `X-ENAME` (no Bearer token needed)
+- **Public Data**: ACL `["*"]` allows any authenticated request
+- **Private Data**: Only listed W3IDs can access
+
+## Webhook Delivery
+
+When data is stored or updated, eVault automatically sends webhooks to all registered platforms.
+
+### Webhook Process
+
+1. **Data Stored**: MetaEnvelope is stored in Neo4j
+2. **Wait 3 Seconds**: Delay prevents webhook ping-pong (same platform receiving its own webhook)
+3. **Get Active Platforms**: Query Registry for list of active platforms
+4. **Filter Requesting Platform**: Exclude the platform that made the request
+5. **Send Webhooks**: POST to each platform's `/api/webhook` endpoint
+
+### Webhook Payload
+
+```json
+{
+ "id": "global-id-123",
+ "w3id": "@user-a.w3id",
+ "schemaId": "550e8400-e29b-41d4-a716-446655440001",
+ "data": {
+ "content": "Hello, world!",
+ "mediaUrls": [],
+ "authorId": "...",
+ "createdAt": "2025-01-24T10:00:00Z"
+ },
+ "evaultPublicKey": "z..."
+}
+```
+
+### Webhook Delivery Details
+
+- **Timeout**: 5 seconds per webhook
+- **Retry**: No automatic retries (fire-and-forget)
+- **Error Handling**: Logs failures but doesn't block the operation
+- **Ordering**: Webhooks are sent in parallel to all platforms - sending to platform A does not block sending to platform B. All webhook POST requests are initiated concurrently.
+
+## Key Binding Certificates
+
+eVault stores public keys for users and issues **key binding certificates** (JWTs) that bind public keys to W3IDs. These certificates serve two important purposes:
+
+1. **Tamper Protection**: Even if HTTPS is not used (though it should be), the JWT signature prevents tampering with public keys in transit. The Registry signs each certificate, ensuring the public key hasn't been modified.
+
+2. **Registry Accountability**: The Registry is accountable for the W3ID-to-public-key binding. By signing the certificates, the Registry attests to the binding between a W3ID and a public key, preventing spoofing of W3ID resolution.
+
+### Certificate Structure
+
+Key binding certificates are JWTs signed by the Registry:
+
+```json
+{
+ "ename": "@user-a.w3id",
+ "publicKey": "zDnaerx9Cp5X2chPZ8n3wK7mN9pQrS7tUvWxYz",
+ "exp": 1737734400,
+ "iat": 1737730800
+}
+```
+
+### Certificate Lifecycle
+
+1. **Provisioning**: When eVault is created, public key is stored and certificate is requested from Registry
+2. **Storage**: Certificates stored in eVault (retrieved via `/whois`)
+3. **Expiration**: Certificates expire after 1 hour
+4. **Verification**: Platforms verify certificates using Registry's JWKS
+
+## Multi-Tenancy
+
+The provisioning layer supports shared tenancy (multiple W3IDs can be provisioned on the same infrastructure). However, each eVault instance is dedicated to a single tenant (one W3ID per eVault):
+
+- **W3ID Index**: Database index on W3ID for fast queries
+- **Isolation**: All queries filtered by W3ID
+- **No Cross-Tenant Access**: Users can only access their own data (unless ACL allows)
+
+
+
+## API Examples
+
+### Storing a Post
+
+```bash
+curl -X POST http://localhost:4000/graphql \
+ -H "Content-Type: application/json" \
+ -H "X-ENAME: @user-a.w3id" \
+ -d '{
+ "query": "mutation { storeMetaEnvelope(input: { ontology: \"550e8400-e29b-41d4-a716-446655440001\", payload: { content: \"Hello!\", authorId: \"...\", createdAt: \"2025-01-24T10:00:00Z\" }, acl: [\"*\"] }) { metaEnvelope { id ontology } } }"
+ }'
+```
+
+### Querying Posts
+
+```bash
+curl -X POST http://localhost:4000/graphql \
+ -H "Content-Type: application/json" \
+ -H "X-ENAME: @user-a.w3id" \
+ -H "Authorization: Bearer " \
+ -d '{
+ "query": "{ findMetaEnvelopesByOntology(ontology: \"550e8400-e29b-41d4-a716-446655440001\") { id parsed } }"
+ }'
+```
+
+### Getting Key Binding Certificates
+
+```bash
+curl -X GET http://localhost:4000/whois \
+ -H "X-ENAME: @user-a.w3id"
+```
+
+## References
+
+- [W3DS Basics](/docs/W3DS%20Basics/getting-started) - Understanding eVault ownership
+- [Authentication](/docs/W3DS%20Protocol/Authentication) - How platforms authenticate users
+- [Signing](/docs/W3DS%20Protocol/Signing) - Signature verification using eVault keys
diff --git a/docs/docs/W3DS Basics/getting-started.md b/docs/docs/W3DS Basics/getting-started.md
index 88f95714..3bfe6e6a 100644
--- a/docs/docs/W3DS Basics/getting-started.md
+++ b/docs/docs/W3DS Basics/getting-started.md
@@ -2,46 +2,358 @@
sidebar_position: 1
---
-# Tutorial Intro
+# W3DS Basics
-Let's discover **Docusaurus in less than 5 minutes**.
+This document provides a deep dive into the core concepts of W3DS (Web 3 Data Spaces), including eVault ownership, data synchronization, and the webhook notification system.
-## Getting Started
+## eVault Ownership Model
-Get started by **creating a new site**.
+In W3DS, **every user, group, and object owns their own eVault**. This is the fundamental principle that enables data portability and platform independence.
-Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
+### What is an eVault?
-### What you'll need
+An **eVault** is a personal data store identified by a **W3ID** (also called an **eName**). It's where all data about a person, group, or object is stored in a standardized format called **MetaEnvelopes**.
-- [Node.js](https://nodejs.org/en/download/) version 20.0 or above:
- - When installing Node.js, you are recommended to check all checkboxes related to dependencies.
+### Ownership Structure
-## Generate a new site
+- **Users**: Each user has their own eVault (e.g., `@user-a.w3id`)
+- **Groups**: Each group has its own eVault (e.g., `@group-1.w3id`)
+- **Objects**: Important objects can have their own eVaults
-Generate a new Docusaurus site using the **classic template**.
+### Key Characteristics
-The classic template will automatically be added to your project after you run the command:
+1. **Persistent Identity**: The W3ID (eName) never changes, even if the eVault URL changes
+2. **User Control**: Users control access to their eVault via ACLs (Access Control Lists). Note: While the ACL system exists, there is currently no platform that allows users to change ACLs for their data - this functionality is planned for future releases.
+3. **Platform Agnostic**: Platforms don't own the data - they only display it
+4. **Decentralized**: Each eVault can be hosted independently
-```bash
-npm init docusaurus@latest my-website classic
+## Data Flow: Platform → eVault → All Platforms
+
+The W3DS data flow ensures that data created on one platform automatically appears on all other platforms. Here's how it works:
+
+### Step-by-Step Flow
+
+Let's trace what happens when **User A creates a post on Blabsy**:
+
+```mermaid
+sequenceDiagram
+ participant UserA as User A
+ participant Blabsy as Blabsy Platform
+ participant BlabsyDB as Blabsy Database
+ participant Web3Adapter as Web3 Adapter
+ participant EVault as User A's eVault
+ participant Pictique as Pictique Platform
+ participant OtherPlatform as Other Platforms
+
+ UserA->>Blabsy: Creates post "Hello, world!"
+ Blabsy->>BlabsyDB: Store post locally
+ BlabsyDB->>Web3Adapter: Entity change detected
+ Web3Adapter->>Web3Adapter: Convert to global schema
+ Web3Adapter->>EVault: storeMetaEnvelope(post data)
+ EVault->>EVault: Store as MetaEnvelope
+ Note over EVault: Wait 3 seconds
(prevent ping-pong)
+ EVault->>Pictique: POST /api/webhook (post data)
+ EVault->>OtherPlatform: POST /api/webhook (post data)
+ Pictique->>Pictique: Convert to local schema
+ Pictique->>Pictique: Create post in local DB
+ OtherPlatform->>OtherPlatform: Convert to local schema
+ OtherPlatform->>OtherPlatform: Create post in local DB
+ Note over UserA,Pictique: User A now has the post
on Pictique without visiting it!
+```
+
+### Detailed Breakdown
+
+#### 1. User Creates Post on Blabsy
+
+User A opens Blabsy and creates a post with the text "Hello, world!". Blabsy stores this in its local PostgreSQL database.
+
+#### 2. Web3 Adapter Detects Change
+
+The platform needs to detect when data changes in its local database. This can be implemented using:
+
+- **Database triggers**: Set up triggers that fire on INSERT/UPDATE/DELETE operations
+- **ORM event listeners**: If using an ORM, hook into entity lifecycle events (afterInsert, afterUpdate, etc.)
+- **Change data capture (CDC)**: Monitor database transaction logs
+- **Application-level hooks**: Call the adapter directly after database operations
+
+The adapter must receive:
+- The changed entity data (as a dictionary/object)
+- The table name or entity type
+- Optionally, a list of participant eNames if the entity involves multiple users
+
+When a post is created, the platform should extract all relevant fields from the database record and pass them to the adapter's change handler along with the table name "posts".
+
+#### 3. Convert to Global Schema
+
+The Web3 Adapter converts the local post data to the global ontology schema. For example:
+
+- Local: `{ text: "Hello, world!", images: [...] }`
+- Global: `{ content: "Hello, world!", mediaUrls: [...] }`
+
+This conversion uses mapping rules defined in `mapping.json` files.
+
+#### 4. Sync to User's eVault
+
+The adapter makes an HTTP POST request to the eVault's GraphQL endpoint. The request must include:
+
+**HTTP Request Details**:
+- **Method**: POST
+- **URL**: `{evaultUrl}/graphql` (where evaultUrl is resolved from the user's eName via Registry)
+- **Headers**:
+ - `Content-Type: application/json`
+ - `X-ENAME: @user-a.w3id` (the owner's eName)
+- **Body**: GraphQL mutation
+
+**GraphQL Mutation**:
+```graphql
+mutation StoreMetaEnvelope($input: MetaEnvelopeInput!) {
+ storeMetaEnvelope(input: $input) {
+ metaEnvelope {
+ id
+ ontology
+ parsed
+ }
+ }
+}
```
-You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.
+**Variables**:
+```json
+{
+ "input": {
+ "ontology": "550e8400-e29b-41d4-a716-446655440001",
+ "payload": {
+ "content": "Hello, world!",
+ "mediaUrls": [],
+ "authorId": "...",
+ "createdAt": "2025-01-24T10:00:00Z"
+ },
+ "acl": ["*"]
+ }
+}
+```
+
+**Response**: The eVault returns the created MetaEnvelope with a global ID that should be stored for future reference.
+
+**Implementation Notes**:
+- Use any HTTP client library in your language (requests in Python, http in Go, fetch in JavaScript, etc.)
+- The GraphQL request is a standard HTTP POST with JSON body
+- The X-ENAME header identifies which eVault to write to
+- Handle network errors and retry logic as needed
+
+#### 5. eVault Stores MetaEnvelope
+
+The eVault stores the data as a MetaEnvelope, which is a flat graph structure of Envelopes. Each field becomes a separate Envelope node in Neo4j.
+
+#### 6. Webhook Delivery (After 3 Second Delay)
+
+After a 3-second delay (to prevent webhook ping-pong), the eVault sends webhooks to all registered platforms **except** the one that made the request (Blabsy).
+
+The webhook payload contains:
+```json
+{
+ "id": "global-id-123",
+ "w3id": "@user-a.w3id",
+ "schemaId": "550e8400-e29b-41d4-a716-446655440001",
+ "data": {
+ "content": "Hello, world!",
+ "mediaUrls": [],
+ "authorId": "...",
+ "createdAt": "2025-01-24T10:00:00Z"
+ }
+}
+```
+
+#### 7. Platforms Receive Webhooks
+
+Pictique and other platforms receive the webhook at their `/api/webhook` endpoint. The platform must implement an HTTP POST handler that:
-The command also installs all necessary dependencies you need to run Docusaurus.
+1. **Parse the webhook payload**: Extract `id`, `w3id`, `schemaId`, and `data` from the JSON request body
-## Start your site
+2. **Find the mapping**: Look up the mapping configuration using the `schemaId`. The mapping defines how to convert between global ontology fields and local database fields.
-Run the development server:
+3. **Convert from global to local schema**: Transform the data using the mapping rules:
+ - Map field names (e.g., `content` → `text`, `mediaUrls` → `images`)
+ - Handle nested references (e.g., `authorId` might need to resolve to a local user ID)
+ - Convert data types if needed
+ - Handle array transformations
-```bash
-cd my-website
-npm run start
+4. **Check if entity exists**: Query the ID mapping table/database to see if a local entity already exists for this global ID. The mapping stores pairs of `(globalId, localId)`.
+
+5. **Create or update entity**:
+ - If mapping exists: Update the existing local entity with the new data
+ - If no mapping: Create a new entity in the local database and store the mapping
+
+6. **Store the ID mapping**: Save the relationship between the global ID and the newly created/updated local entity ID for future lookups.
+
+7. **Return success**: Send HTTP 200 OK response to acknowledge receipt of the webhook.
+
+**Implementation Requirements**:
+- HTTP server with POST endpoint handler
+- JSON parsing library
+- Database access (SQL or NoSQL)
+- ID mapping storage (database table, key-value store, or in-memory cache with persistence)
+- Field mapping logic (can be implemented as a simple dictionary/object transformation)
+
+#### 8. Result
+
+User A now has the post on Pictique (and all other platforms) without ever visiting them!
+
+## Webhook Notification System
+
+The webhook system is how eVaults notify platforms of data changes.
+
+### Webhook Delivery Process
+
+```mermaid
+flowchart TD
+ Start([Data Stored in eVault]) --> Delay[Wait 3 seconds]
+ Delay --> GetPlatforms[Get Active Platforms from Registry]
+ GetPlatforms --> Filter[Filter out Requesting Platform]
+ Filter --> SendWebhooks[Send POST to /api/webhook]
+ SendWebhooks --> Success{Success?}
+ Success -->|Yes| LogSuccess[Log Success]
+ Success -->|No| LogError[Log Error Continue]
+ LogSuccess --> End([Complete])
+ LogError --> End
```
-The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.
+### Webhook Payload Structure
+
+Every webhook contains:
+
+- **id**: The global ID of the MetaEnvelope
+- **w3id**: The eName (W3ID) of the owner
+- **schemaId**: The ontology schema ID (UUID)
+- **data**: The actual data in global ontology format
+- **evaultPublicKey**: The eVault's public key (for verification)
+
+### Platform Webhook Handling
+
+Platforms must implement a webhook endpoint that:
+
+1. Receives the webhook payload
+2. Finds the mapping using `schemaId`
+3. Converts global data to local schema using `fromGlobal()`
+4. Checks if entity exists using global ID mapping
+5. Creates or updates the entity
+6. Stores the ID mapping
+
+See the [Webhook Controller Guide](/docs/Post%20Platform%20Guide/webhook-controller) for implementation details.
+
+## MetaEnvelopes and Ontology Schemas
+
+### MetaEnvelopes
+
+A **MetaEnvelope** is the storage format in eVaults. It's a flat graph structure where:
+
+- Each MetaEnvelope represents one entity (post, user, message, etc.)
+- Each field becomes a separate Envelope node
+- Envelopes are linked to the MetaEnvelope via `LINKS_TO` relationships
+
+### Ontology Schemas
+
+**Ontology schemas** define the global data format. They're JSON Schema files that specify:
+
+- Field names and types
+- Required fields
+- Validation rules
+- Schema IDs (UUIDs)
+
+Example: `SocialMediaPost` schema defines fields like `content`, `mediaUrls`, `authorId`, etc.
+
+All platforms must map their local schemas to these global schemas. See [Mapping Rules](/docs/Post%20Platform%20Guide/mapping-rules) for details.
+
+## W3ID (eName) System
+
+**W3ID** (also called **eName**) is the persistent identifier for users, groups, and objects.
+
+### Format
+
+- Global IDs: `@` (e.g., `@e4d909c2-5d2f-4a7d-9473-b34b6c0f1a5a`)
+- Local IDs: `@/` (for objects within an eVault)
+
+### Resolution
+
+W3IDs are resolved to eVault URLs via the Registry Service:
+
+```
+GET /resolve?w3id=@user-a.w3id
+→ Returns: https://evault.example.com/users/user-a
+```
+
+### Key Properties
+
+1. **Persistent**: Never changes, even if eVault URL changes
+2. **Globally Unique**: UUID-based ensures uniqueness
+3. **Loosely Bound to Keys**: Can rotate keys without changing W3ID
+4. **Resolvable**: Registry maps W3ID to current eVault URL
+
+## Data Ownership and ACLs
+
+### Access Control Lists (ACLs)
+
+ACLs determine who can access data in an eVault. Common patterns:
+
+- `["*"]`: Public access (anyone can read)
+- `["@user-a.w3id"]`: Only User A can access
+- `["@user-a.w3id", "@user-b.w3id"]`: User A and User B can access
+
+### Ownership Model
+
+- **Data Creator**: The user who creates data owns it
+- **eVault Owner**: The user whose eVault stores the data
+- **Platform Access**: Platforms can read/write based on ACLs
+
+## Platform Independence
+
+One of the key benefits of W3DS is **platform independence**:
+
+1. **No Vendor Lock-in**: Users can switch platforms without losing data
+2. **Multi-Platform Presence**: Data automatically appears on all platforms
+3. **Platform Competition**: Platforms compete on features, not data ownership
+4. **User Choice**: Users choose platforms based on UX, not data availability
+
+## Example: Complete Post Flow
+
+Here's a complete example showing all components working together:
+
+```mermaid
+graph LR
+ subgraph UserA["User A"]
+ A1[Creates Post]
+ end
+
+ subgraph Blabsy["Blabsy Platform"]
+ B1[Local DB]
+ B2[Web3 Adapter]
+ end
+
+ subgraph EVault["User A's eVault"]
+ E1[Store MetaEnvelope]
+ E2[Webhook System]
+ end
+
+ subgraph Pictique["Pictique Platform"]
+ P1[Webhook Handler]
+ P2[Local DB]
+ end
+
+ A1 -->|1. Create| B1
+ B1 -->|2. Change Event| B2
+ B2 -->|3. Convert Schema| E1
+ E1 -->|4. Store| E2
+ E2 -->|5. Webhook| P1
+ P1 -->|6. Convert & Store| P2
+
+ style A1 fill:#e1f5ff,color:#000000
+ style E1 fill:#fff4e1,color:#000000
+ style P2 fill:#e8f5e9,color:#000000
+```
-The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.
+## Next Steps
-Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.
+- Learn about [Authentication](/docs/W3DS%20Protocol/Authentication) - How users authenticate
+- Understand [Signing](/docs/W3DS%20Protocol/Signing) - Signature creation and verification
+- Explore [Signature Formats](/docs/W3DS%20Protocol/Signature-Formats) - Cryptographic details
+- Build a platform with the [Post Platform Guide](/docs/Post%20Platform%20Guide/getting-started)
diff --git a/docs/docs/W3DS Protocol/Authentication.md b/docs/docs/W3DS Protocol/Authentication.md
new file mode 100644
index 00000000..da0e1017
--- /dev/null
+++ b/docs/docs/W3DS Protocol/Authentication.md
@@ -0,0 +1,441 @@
+---
+sidebar_position: 1
+---
+
+# Authentication
+
+W3DS uses cryptographic signature-based authentication. Users authenticate with platforms by signing a session ID with their private key, which platforms verify using public keys stored in eVaults.
+
+## Overview
+
+Unlike traditional password-based authentication, W3DS uses **cryptographic signatures** for authentication. This provides:
+
+- **No passwords**: Users never share secrets with platforms
+- **Cryptographic proof**: Platforms can cryptographically verify user identity
+- **Key-based**: Uses ECDSA P-256 keys managed by the eID wallet
+- **Decentralized**: Public keys stored in user's eVault, verified via Registry
+
+## Authentication Flow
+
+The authentication process follows these steps:
+
+```mermaid
+sequenceDiagram
+ participant User as User (eID Wallet)
+ participant Platform as Platform API
+ participant Registry as Registry Service
+ participant EVault as User's eVault
+ participant Validator as Signature Validator
+
+ User->>Platform: 1. Request login
+ Platform->>Platform: 2. Generate session ID
+ Platform-->>User: 3. Return w3ds://auth URI
(contains session ID)
+ User->>User: 4. Sign session ID
with private key
+ User->>Platform: 5. POST to redirect URL
(w3id, session, signature, appVersion)
+ Platform->>Validator: 6. verifySignature()
+ Validator->>Registry: 7. GET /resolve?w3id=@user.w3id
+ Registry-->>Validator: 8. eVault URL
+ Validator->>EVault: 9. GET /whois
(X-ENAME: @user.w3id)
+ EVault-->>Validator: 10. keyBindingCertificates[]
+ Validator->>Registry: 11. GET /.well-known/jwks.json
+ Registry-->>Validator: 12. JWKS
+ Validator->>Validator: 13. Verify JWT with JWKS
+ Validator->>Validator: 14. Extract public key from JWT
+ Validator->>Validator: 15. Verify signature
(ECDSA P-256, SHA-256)
+ Validator-->>Platform: 16. Verification result
+ alt Signature Valid
+ Platform->>Platform: 17. Create user session
+ Platform-->>User: 18. Authentication token
+ else Signature Invalid
+ Platform-->>User: 19. Authentication failed
+ end
+```
+
+## Protocol Steps
+
+### Step 1: Platform Requests Session
+
+When a user wants to log in, the platform must:
+
+1. **Generate a unique session identifier**: Use a cryptographically secure random UUID (version 4). This session ID will be signed by the user, so it must be unique and unpredictable.
+
+2. **Construct the redirect URL**: Build the full URL where the eID wallet will POST the signed session data. This is typically `{platformBaseUrl}/api/auth/login` or similar. The wallet will make an HTTP POST request to this URL with the signed authentication data.
+
+3. **Build the w3ds://auth URI**: Create a URI with the following format:
+ ```text
+ w3ds://auth?redirect={redirectUrl}&session={sessionId}&platform={platformName}
+ ```
+ - `redirect`: URL-encoded redirect endpoint where the eID wallet will POST the signed session (this is the callback URL)
+ - `session`: The generated session UUID
+ - `platform`: Platform identifier (for display purposes)
+
+4. **Return JSON response**: Send a JSON object with the `uri` field containing the w3ds://auth URI.
+
+**HTTP Endpoint**: `GET /api/auth/offer`
+
+**Request**: No body required, standard HTTP GET
+
+**Response Format**:
+```json
+{
+ "uri": "w3ds://auth?redirect=https://blabsy.example.com/api/auth&session=550e8400-e29b-41d4-a716-446655440000&platform=blabsy"
+}
+```
+
+**Implementation Requirements**:
+- Generate UUID v4 (128-bit random UUID)
+- URL-encode the redirect parameter
+- Return JSON with Content-Type: application/json
+- Store the session ID temporarily (in memory, cache, or database) to validate it later
+
+**Reference Implementation** (TypeScript):
+```typescript
+getOffer = async (req: Request, res: Response) => {
+ const url = new URL(
+ "/api/auth",
+ process.env.PUBLIC_BLABSY_BASE_URL
+ ).toString();
+ const session = uuidv4();
+ const offer = `w3ds://auth?redirect=${url}&session=${session}&platform=blabsy`;
+ res.json({ uri: offer });
+};
+```
+
+### Step 2: User Signs Session ID
+
+The user's eID wallet signs the session ID using their private key. The signing process:
+
+1. **Parse the w3ds://auth URI**: Extract the `session` parameter from the query string. The session ID is a UUID string that must be signed exactly as received.
+
+2. **Hash the session ID**: Convert the session ID string to bytes using UTF-8 encoding, then compute SHA-256 hash. This produces a 32-byte hash digest.
+
+3. **Sign the hash**: Use ECDSA P-256 (secp256r1 curve) to sign the hash with the user's private key:
+ - **Algorithm**: ECDSA
+ - **Curve**: P-256 (secp256r1, NIST P-256)
+ - **Hash**: SHA-256
+ - **Result**: A signature consisting of two 32-byte integers (r and s), concatenated to form a 64-byte raw signature
+
+4. **Encode the signature**:
+ - **Software keys**: Encode the 64-byte signature using base64 encoding
+ - **Hardware keys**: Encode using multibase base58btc (starts with 'z' prefix)
+
+**Cryptographic Details**:
+- **Curve**: secp256r1 (NIST P-256)
+- **Hash Algorithm**: SHA-256
+- **Signature Format**: Raw 64-byte (r || s), where r and s are each 32 bytes
+- **Encoding**: Base64 (software) or Multibase base58btc (hardware)
+
+**Library Requirements** (for wallet implementation):
+- ECDSA signing library (crypto libraries in most languages support this)
+- SHA-256 hashing
+- Base64 or base58btc encoding
+- Key management for storing/accessing private keys securely
+
+For detailed signature format information, see the [Signature Formats documentation](/docs/W3DS%20Protocol/Signature-Formats).
+
+### Step 3: eID Wallet POSTs Signed Session
+
+After the user signs the session ID, the eID wallet makes an HTTP POST request to the `redirect` URL specified in the `w3ds://auth` URI. The platform receives this POST request with the signed session data:
+
+**Endpoint**: The `redirect` URL from the `w3ds://auth` URI (e.g., `POST /api/auth/login`)
+
+**What the eID Wallet POSTs**:
+
+**Request Headers**:
+- `Content-Type: application/json`
+
+**Request Body** (JSON):
+```json
+{
+ "w3id": "@user-a.w3id",
+ "session": "550e8400-e29b-41d4-a716-446655440000",
+ "signature": "xK3vJZQ2F3k5L8mN9pQrS7tUvW1xY3zA5bC7dE9fG1hIjKlMnOpQrStUvWxYz==",
+ "appVersion": "0.4.0"
+}
+```
+
+**Field Descriptions**:
+- `w3id`: The user's W3ID (eName) identifier, always starts with '@'
+- `session`: The session UUID that was generated in Step 1
+- `signature`: The base64 or multibase-encoded signature of the session ID
+- `appVersion`: Optional version string for compatibility checking
+
+> **Note on `appVersion`**: This field is temporary and will be sunset after the rollout is completed. It was added because some users were on outdated eID wallet versions that handled signing differently. Once all users have updated to compatible versions, this field will be removed from the protocol.
+
+**Platform Processing Steps**:
+
+1. **Validate Input**: Ensure all required fields (w3id, session, signature) are present and non-empty. Return 400 Bad Request if validation fails.
+
+2. **Validate Session**: Check that the session ID matches one that was recently generated (within the last 5 minutes). Reject duplicate or expired sessions. This prevents replay attacks.
+
+3. **Verify Signature**: Call the signature verification function (see [Signing documentation](/docs/W3DS%20Protocol/Signing)) with:
+ - The user's eName
+ - The signature string
+ - The session ID as the payload
+ - The Registry base URL
+
+4. **Handle Verification Result**:
+ - If verification succeeds: Create a session token (JWT, session cookie, or platform-specific token) and return it
+ - If verification fails: Return 401 Unauthorized with an error message
+
+5. **Optional App Version Check**: Validate that appVersion meets minimum requirements. Return 400 if version is too old. Note: This check is temporary and will be removed after the rollout is completed (see note on `appVersion` field above).
+
+**Response on Success** (200 OK):
+```json
+{
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
+}
+```
+
+**Response on Failure** (401 Unauthorized):
+```json
+{
+ "error": "Invalid signature",
+ "message": "Signature verification failed"
+}
+```
+
+**Reference Implementation** (TypeScript):
+```typescript
+login = async (req: Request, res: Response) => {
+ const { w3id, session, appVersion, signature } = req.body;
+
+ if (!w3id || !session || !signature) {
+ return res.status(400).json({ error: "Missing required fields" });
+ }
+
+ // Verify signature (see Signing documentation)
+ const verificationResult = await verifySignature({
+ eName: w3id,
+ signature: signature,
+ payload: session,
+ registryBaseUrl: process.env.PUBLIC_REGISTRY_URL,
+ });
+
+ if (!verificationResult.valid) {
+ return res.status(401).json({
+ error: "Invalid signature",
+ message: verificationResult.error
+ });
+ }
+
+ // Create authentication token
+ const token = await auth().createCustomToken(w3id);
+ res.status(200).json({ token });
+};
+```
+
+## Platform Implementation
+
+### Example: Blabsy Auth Controller
+
+**Version Validation Helper Function**:
+
+Before implementing the auth controller, you'll need a function to compare semantic versions. Here's an implementation example:
+
+```typescript
+/**
+ * Compares two semantic version strings (e.g., "1.2.3")
+ * @param appVersion - The version to check (e.g., "0.3.5")
+ * @param minVersion - The minimum required version (e.g., "0.4.0")
+ * @returns true if appVersion >= minVersion, false otherwise
+ */
+function isVersionValid(appVersion: string | undefined, minVersion: string): boolean {
+ if (!appVersion) {
+ return false; // Missing version is considered invalid
+ }
+
+ // Parse versions into [major, minor, patch] arrays
+ const parseVersion = (version: string): number[] => {
+ return version.split('.').map(Number).slice(0, 3);
+ };
+
+ const app = parseVersion(appVersion);
+ const min = parseVersion(minVersion);
+
+ // Compare lexicographically: major, then minor, then patch
+ for (let i = 0; i < 3; i++) {
+ if (app[i] > min[i]) {
+ return true; // appVersion is newer
+ }
+ if (app[i] < min[i]) {
+ return false; // appVersion is older
+ }
+ // Continue to next component if equal
+ }
+
+ return true; // Versions are equal
+}
+```
+
+**Language-Agnostic Implementation**:
+
+The version comparison logic can be implemented in any language:
+
+1. **Parse versions**: Split version strings by '.' into arrays of integers (major, minor, patch)
+2. **Compare components**: Compare major, then minor, then patch lexicographically
+3. **Return result**: Return `true` if appVersion >= minVersion, `false` otherwise
+
+**Library Alternatives**:
+- **Node.js/TypeScript**: Use `semver` package: `semver.gte(appVersion, minVersion)`
+- **Python**: Use `packaging.version`: `Version(appVersion) >= Version(minVersion)`
+- **Go**: Use `golang.org/x/mod/semver`: `semver.Compare(appVersion, "v"+minVersion) >= 0`
+- **Java**: Use `org.apache.maven.artifact.versioning.ComparableVersion`
+
+```typescript
+import { Request, Response } from "express";
+import { v4 as uuidv4 } from "uuid";
+import { verifySignature } from "signature-validator";
+
+export class AuthController {
+ // Step 1: Generate session and return w3ds://auth URI
+ getOffer = async (req: Request, res: Response) => {
+ const url = new URL(
+ "/api/auth",
+ process.env.PUBLIC_BLABSY_BASE_URL
+ ).toString();
+ const session = uuidv4();
+ const offer = `w3ds://auth?redirect=${url}&session=${session}&platform=blabsy`;
+ res.json({ uri: offer });
+ };
+
+ // Step 2: Verify signature and authenticate
+ login = async (req: Request, res: Response) => {
+ const { w3id, session, signature, appVersion } = req.body;
+
+ // Validate input
+ if (!w3id || !session || !signature) {
+ return res.status(400).json({ error: "Missing required fields" });
+ }
+
+ // Verify app version
+ if (!isVersionValid(appVersion, "0.4.0")) {
+ return res.status(400).json({
+ error: "App version too old"
+ });
+ }
+
+ // Verify signature
+ const verificationResult = await verifySignature({
+ eName: w3id,
+ signature: signature,
+ payload: session,
+ registryBaseUrl: process.env.PUBLIC_REGISTRY_URL,
+ });
+
+ if (!verificationResult.valid) {
+ return res.status(401).json({
+ error: "Invalid signature",
+ message: verificationResult.error
+ });
+ }
+
+ // Create authentication token
+ const token = await auth().createCustomToken(w3id);
+ res.status(200).json({ token });
+ };
+}
+```
+
+### Example: Pictique Auth Controller
+
+```typescript
+import { verifySignature } from "signature-validator";
+import { signToken } from "../utils/jwt";
+
+export class AuthController {
+ login = async (req: Request, res: Response) => {
+ const { w3id, session, signature } = req.body;
+
+ // Verify signature
+ const verificationResult = await verifySignature({
+ eName: w3id,
+ signature: signature,
+ payload: session,
+ registryBaseUrl: process.env.PUBLIC_REGISTRY_URL,
+ });
+
+ if (!verificationResult.valid) {
+ return res.status(401).json({
+ error: "Invalid signature"
+ });
+ }
+
+ // Find or create user
+ let user = await this.userService.findByEname(w3id);
+ if (!user) {
+ throw new Error("User not found");
+ }
+
+ // Generate JWT token
+ const token = signToken({ userId: user.id });
+
+ res.status(200).json({
+ user: {
+ id: user.id,
+ ename: user.ename,
+ },
+ token,
+ });
+ };
+}
+```
+
+## Security Considerations
+
+### 1. Session ID Uniqueness
+
+- Session IDs must be **cryptographically random** (use UUID v4)
+- Each session ID should be used **only once**
+- Expire session IDs after a reasonable time (e.g., 5 minutes)
+
+**Why this matters**: Without these protections, an attacker who intercepts a signed session ID could reuse it to authenticate as the user. This is called a **replay attack**. If session IDs:
+- Are predictable (not cryptographically random): Attackers could guess or generate valid session IDs
+- Can be reused: An intercepted signed session ID could be used multiple times to gain unauthorized access
+- Don't expire: An old intercepted session ID could be used indefinitely, even after the user has logged out or changed their keys
+
+By enforcing uniqueness, one-time use, and expiration, platforms ensure that even if a signed session ID is intercepted, it cannot be used after it expires or has already been consumed, significantly reducing the window of vulnerability.
+
+### 2. Signature Replay Prevention
+
+- Include **nonces or timestamps** in signed payloads
+- Platforms should track used session IDs
+- Reject duplicate session IDs
+
+### 3. App Version Validation
+
+> **Note**: App version validation is temporary and will be removed after the rollout is completed. It was added because some users were on outdated eID wallet versions that handled signing differently.
+
+- Platforms should validate app version to ensure compatibility (during the rollout period)
+- Reject authentication from outdated app versions
+- Provide clear error messages for version mismatches
+
+### 4. Error Handling
+
+- **Never expose sensitive information** in error messages
+- Log verification failures for security monitoring
+- Return generic errors to prevent information leakage
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Session validation fails**
+ - Check that session IDs are stored and retrieved correctly
+ - Verify session expiration logic
+ - Ensure session IDs are not reused
+
+2. **Authentication token creation fails**
+ - Verify token generation library is configured correctly
+ - Check that user data is available after signature verification
+ - Ensure token expiration is set appropriately
+
+3. **App version validation issues** (temporary - will be removed after rollout)
+ - Verify version comparison logic
+ - Check that minimum required version is correctly configured
+ - Ensure version strings are parsed correctly
+
+## References
+
+- [Signing](/docs/W3DS%20Protocol/Signing) - Signature creation and verification details
+- [Signature Formats](/docs/W3DS%20Protocol/Signature-Formats) - Detailed signature format documentation
diff --git a/docs/docs/W3DS Protocol/Signatures.md b/docs/docs/W3DS Protocol/Signature-Formats.md
similarity index 99%
rename from docs/docs/W3DS Protocol/Signatures.md
rename to docs/docs/W3DS Protocol/Signature-Formats.md
index a636aa7c..318517d4 100644
--- a/docs/docs/W3DS Protocol/Signatures.md
+++ b/docs/docs/W3DS Protocol/Signature-Formats.md
@@ -1,8 +1,8 @@
---
-sidebar_position: 2
+sidebar_position: 3
---
-# Signatures
+# Signature Formats
This document explains the signature formats used in the eID wallet and how to verify signatures using public keys from eVault.
diff --git a/docs/docs/W3DS Protocol/Signing.md b/docs/docs/W3DS Protocol/Signing.md
new file mode 100644
index 00000000..3e511863
--- /dev/null
+++ b/docs/docs/W3DS Protocol/Signing.md
@@ -0,0 +1,511 @@
+---
+sidebar_position: 2
+---
+
+# Signing
+
+This document explains the `w3ds://sign` protocol for requesting arbitrary signatures from users. This protocol allows platforms to request users to sign custom data such as documents, votes, references, or any other payload.
+
+## Overview
+
+The `w3ds://sign` protocol enables platforms to request cryptographic signatures from users through their eID wallet. Users scan a QR code containing a `w3ds://sign` URI, review what they're signing, and confirm. The wallet signs the session ID and sends it back to the platform for verification.
+
+**Note**: This document covers arbitrary signature requests using the `w3ds://sign` protocol. For authentication signatures used during login, see the [Authentication documentation](/docs/W3DS%20Protocol/Authentication).
+
+## Protocol Flow
+
+```mermaid
+sequenceDiagram
+ participant User as User
+ participant Platform as Platform API
+ participant Wallet as eID Wallet
+ participant Registry as Registry Service
+ participant EVault as User's eVault
+ participant Validator as Signature Validator
+
+ User->>Platform: 1. Request to sign something
(e.g., sign a reference)
+ Platform->>Platform: 2. Create signing session
(generate sessionId, encode data)
+ Platform->>Platform: 3. Build w3ds://sign URI
(session, data, redirect_uri)
+ Platform-->>User: 4. Return QR code
w3ds://sign?session=...&data=...&redirect_uri=...
+ User->>Wallet: 5. Scan QR code
+ Wallet->>Wallet: 6. Parse w3ds://sign URI
+ Wallet->>Wallet: 7. Decode base64 data
+ Wallet->>User: 8. Show signing request
(message, context)
+ User->>Wallet: 9. Confirm signing
+ Wallet->>Wallet: 10. Sign sessionId
(ECDSA P-256, SHA-256)
+ Wallet->>Platform: 11. POST to redirect_uri
(sessionId, signature, w3id, message)
+ Platform->>Validator: 12. verifySignature()
+ Validator->>Registry: 13. GET /resolve?w3id=@user.w3id
+ Registry-->>Validator: 14. eVault URL
+ Validator->>EVault: 15. GET /whois
(X-ENAME: @user.w3id)
+ EVault-->>Validator: 16. keyBindingCertificates[]
+ Validator->>Registry: 17. GET /.well-known/jwks.json
+ Registry-->>Validator: 18. JWKS
+ Validator->>Validator: 19. Verify JWT with JWKS
+ Validator->>Validator: 20. Extract public key from JWT
+ Validator->>Validator: 21. Verify signature
(ECDSA P-256, SHA-256)
+ Validator-->>Platform: 22. Verification result
+ alt Signature Valid
+ Platform->>Platform: 23. Process signed request
+ Platform-->>User: 24. Success response
+ else Signature Invalid
+ Platform-->>User: 25. Error response
+ end
+```
+
+### Step 1: Platform Creates Signing Session
+
+The platform creates a signing session with:
+
+1. **Generate Session ID**: Create a unique UUID for the signing session
+2. **Prepare Data**: Create a JSON object containing:
+ - `message`: Human-readable description of what's being signed
+ - `sessionId`: The session UUID
+ - Any additional context-specific data
+3. **Encode Data**: Base64-encode the JSON string
+4. **Build w3ds://sign URI**: Create URI with format:
+ ```text
+ w3ds://sign?session={sessionId}&data={base64Data}&redirect_uri={encodedRedirectUri}
+ ```
+ - `session`: The session UUID
+ - `data`: Base64-encoded JSON containing the message and context
+ - `redirect_uri`: URL-encoded endpoint where the eID wallet will POST the signed payload (this is the callback URL)
+5. **Store Session**: Store session in memory/database with expiration (typically 15 minutes)
+
+**Implementation Requirements**:
+- Generate UUID v4 for session ID
+- Create JSON object with message and context data
+- Base64-encode the JSON string
+- URL-encode the redirect_uri parameter
+- Store session with expiration time (15 minutes recommended)
+
+**Example** (from eReputation platform - TypeScript):
+```typescript
+// Create signing session
+const sessionId = crypto.randomUUID();
+const messageData = JSON.stringify({
+ message: `Sign reference for ${targetType}: ${targetName}`,
+ sessionId: sessionId,
+ referenceId: referenceId
+});
+
+const base64Data = Buffer.from(messageData).toString('base64');
+const redirectUri = `${apiBaseUrl}/api/references/signing/callback`;
+
+const qrData = `w3ds://sign?session=${sessionId}&data=${base64Data}&redirect_uri=${encodeURIComponent(redirectUri)}`;
+```
+
+**Language-Agnostic Implementation**:
+- Use any UUID library to generate session ID (uuid in Node.js, uuid in Python, google/uuid in Go, etc.)
+- Use standard JSON serialization
+- Use standard base64 encoding (base64 in Python, base64 in Go, Buffer in Node.js, etc.)
+- Use URL encoding for the redirect_uri parameter
+- Store session in memory (Map/dictionary) or database with expiration tracking
+
+**HTTP Endpoint**: `POST /api/{resource}/signing/session`
+
+**Request Body**:
+```json
+{
+ "referenceId": "ref-123",
+ "referenceData": {
+ "targetType": "user",
+ "targetName": "John Doe",
+ "content": "Great developer"
+ }
+}
+```
+
+**Response**:
+```json
+{
+ "sessionId": "550e8400-e29b-41d4-a716-446655440000",
+ "qrData": "w3ds://sign?session=...&data=...&redirect_uri=...",
+ "expiresAt": "2025-01-24T10:15:00Z"
+}
+```
+
+### Step 2: User Scans QR Code
+
+The user scans the QR code with their eID wallet. The wallet:
+
+1. **Parses URI**: Extracts `session`, `data`, and `redirect_uri` parameters
+2. **Decodes Data**: Base64-decodes the data to get the JSON message
+3. **Displays Request**: Shows the user what they're signing (message, context)
+4. **Waits for Confirmation**: User reviews and confirms
+
+### Step 3: Wallet Signs the Session ID
+
+When the user confirms:
+
+1. **Get Session ID**: Extract session ID from the URI parameters
+2. **Sign Session ID**: Sign the session ID string (not the full data) using:
+ - Key ID: `"default"`
+ - Context: `"onboarding"` (real users) or `"pre-verification"` (test users)
+ - Algorithm: ECDSA P-256 with SHA-256
+3. **Encode Signature**: Base64 (software keys) or multibase (hardware keys)
+
+**Important**: The wallet signs the **session ID**, not the decoded data. The platform uses the session ID to look up the original context.
+
+### Step 4: eID Wallet POSTs Signed Payload
+
+After the user confirms signing, the eID wallet makes an HTTP POST request to the `redirect_uri` specified in the `w3ds://sign` URI. The platform receives this POST request with the signed payload:
+
+**Endpoint**: The `redirect_uri` from the `w3ds://sign` URI (e.g., `POST /api/references/signing/callback`)
+
+**What the eID Wallet POSTs**:
+```http
+POST {redirect_uri} HTTP/1.1
+Content-Type: application/json
+
+{
+ "sessionId": "550e8400-e29b-41d4-a716-446655440000",
+ "signature": "xK3vJZQ2F3k5L8mN9pQrS7tUvW1xY3zA5bC7dE9fG1hIjKlMnOpQrStUvWxYz==",
+ "w3id": "@user-a.w3id",
+ "message": "550e8400-e29b-41d4-a716-446655440000"
+}
+```
+
+**Field Descriptions**:
+- `sessionId`: The session UUID from the signing request
+- `signature`: The base64 or multibase-encoded signature of the session ID
+- `w3id`: The user's eName (W3ID)
+- `message`: The session ID that was signed (for verification)
+
+### Step 5: Platform Receives and Verifies Signed Payload
+
+The platform receives the POST request from the eID wallet at the `redirect_uri` endpoint and:
+
+1. **Validate Input**: Check all required fields are present
+2. **Lookup Session**: Retrieve the signing session using `sessionId`
+3. **Verify Session**: Check session is still valid (not expired, status is "pending")
+4. **Verify Signature**: Use signature verification (see [Signature Verification](#signature-verification)) with:
+ - eName: `w3id` from request
+ - Signature: `signature` from request
+ - Payload: `message` (session ID) from request
+5. **Verify User**: Ensure the signing user matches the expected user (security check)
+6. **Process Request**: Perform the action (record signature, update status, etc.)
+7. **Update Session**: Mark session as "completed" or "security_violation"
+
+**Implementation Requirements**:
+- Parse JSON request body
+- Validate all required fields (sessionId, signature, w3id, message)
+- Lookup session from storage using sessionId
+- Check session validity (exists, not expired, status is "pending")
+- Verify signature using signature verification process
+- Verify user identity matches expected user
+- Process the signed request (update database, trigger actions, etc.)
+- Update session status
+- Return appropriate HTTP response
+
+**Example Implementation** (from eReputation - TypeScript):
+```typescript
+async handleSignedPayload(req: Request, res: Response) {
+ const { sessionId, signature, w3id, message } = req.body;
+
+ // Validate required fields
+ if (!sessionId || !signature || !w3id || !message) {
+ return res.status(400).json({ error: "Missing required fields" });
+ }
+
+ // Process the signed payload
+ const result = await signingService.processSignedPayload(
+ sessionId,
+ signature,
+ w3id,
+ message
+ );
+
+ if (result.success) {
+ res.json({ success: true, data: result });
+ } else {
+ res.status(200).json({
+ success: false,
+ error: result.error
+ });
+ }
+}
+```
+
+**Language-Agnostic Implementation**:
+- Use any HTTP server framework (Express, FastAPI, Gin, etc.)
+- Parse JSON from request body
+- Use signature verification library or implement verification (see [Signature Verification](#signature-verification))
+- Store sessions in memory (Map/dictionary) or database
+- Implement session expiration checking
+- Return JSON responses with appropriate HTTP status codes
+
+### Security Considerations
+
+1. **Session Expiration**: Sessions should expire after a reasonable time (15 minutes recommended)
+2. **One-Time Use**: Each session ID should only be used once
+3. **User Verification**: Verify that the signing user matches the expected user
+4. **Signature Verification**: Always verify signatures using eVault before processing
+5. **Payload Validation**: Ensure the signed message (session ID) matches the stored session
+
+### Use Cases
+
+- **Document Signing**: Sign contracts, agreements, or documents
+- **Voting**: Sign votes in polls or elections
+- **References**: Sign references or recommendations
+- **Approvals**: Sign approvals for actions or requests
+- **Custom Actions**: Any platform-specific signing requirement
+
+## Signature Verification
+
+Signature verification is a multi-step process that platforms must implement. The platform verifies that a signature was created by the user who owns a specific eName.
+
+### Overview
+
+The verification process:
+1. Resolve eVault URL from Registry using eName
+2. Fetch key binding certificates from eVault
+3. Verify JWT certificates using Registry's public keys
+4. Extract public keys from verified certificates
+5. Verify signature using the public key
+
+### Step 1: Resolve eVault URL
+
+Make an HTTP GET request to the Registry service:
+
+**Request**:
+```http
+GET {registryBaseUrl}/resolve?w3id=@user-a.w3id
+```
+
+**Response**:
+```json
+{
+ "evaultUrl": "https://evault.example.com/users/user-a"
+}
+```
+
+**Implementation**: Use any HTTP client library. Parse the JSON response to extract the `evaultUrl` field. Handle network errors and timeouts appropriately.
+
+### Step 2: Get Key Binding Certificates
+
+Make an HTTP GET request to the eVault's `/whois` endpoint:
+
+**Request**:
+```http
+GET {evaultUrl}/whois
+Headers:
+ X-ENAME: @user-a.w3id
+```
+
+**Response**:
+```json
+{
+ "keyBindingCertificates": [
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
+ ]
+}
+```
+
+Key binding certificates are JWTs that contain:
+- The user's eName
+- The user's public key (multibase encoded)
+- Expiration time (1 hour validity)
+- Signature from Registry
+
+### Step 3: Verify JWT Certificates
+
+For each certificate, perform these steps:
+
+1. **Fetch Registry JWKS**: Make HTTP GET request to `{registryBaseUrl}/.well-known/jwks.json`. The response is a JSON object containing public keys in JWK (JSON Web Key) format.
+
+2. **Parse JWT**: Split the JWT into three parts (header.payload.signature) using '.' as delimiter. Base64url-decode the header and payload.
+
+3. **Verify JWT Signature**:
+ - Find the matching key from JWKS using the `kid` (key ID) from JWT header
+ - The JWT is signed with ECDSA P-256 using the Registry's private key
+ - Verify the signature using the corresponding public key from JWKS
+ - Use a JWT library in your language or implement ECDSA verification manually
+
+4. **Check Expiration**: Verify the `exp` claim in the JWT payload. Certificates have 1-hour validity.
+
+5. **Extract Public Key**: Get the `publicKey` field from the JWT payload. This is the user's public key in multibase format.
+
+**JWT Payload Structure**:
+```json
+{
+ "ename": "@user-a.w3id",
+ "publicKey": "zDnaerx9Cp5X2chPZ8n3wK7mN9pQrS7tUvW1xY3zA5bC7dE9fG1hIjKlMnOpQrStUvWxYz",
+ "exp": 1737734400,
+ "iat": 1737730800
+}
+```
+
+**Library Requirements**: JWT library that supports ECDSA P-256 verification (most JWT libraries do: jose in Node.js, PyJWT in Python, jwt-go in Go, etc.)
+
+### Step 4: Decode Public Key
+
+The public key from the JWT is in multibase format. Decode it based on the first character:
+
+- **'z' prefix**: base58btc encoding - use a base58btc decoder library
+- **'m' prefix**: base64 encoding - use standard base64 decoder
+- **'f' prefix**: hex encoding - decode hex string to bytes
+
+After decoding, the public key should be in one of these formats:
+- **SPKI DER format**: DER-encoded SubjectPublicKeyInfo structure (most common)
+- **Raw uncompressed**: 65 bytes starting with 0x04 followed by 64 bytes of coordinates
+
+**Library Requirements**:
+- Multibase decoder (or implement base58btc/base64/hex decoding)
+- ASN.1 parser if the key is in DER format (most crypto libraries handle this automatically)
+
+### Step 5: Verify Signature
+
+Use your language's cryptographic library to verify the ECDSA signature:
+
+1. **Import Public Key**: Load the decoded public key into your crypto library. Most libraries accept:
+ - SPKI DER format (preferred)
+ - Raw uncompressed format (65 bytes)
+ - PEM format (if your library supports it)
+
+2. **Prepare the Message**:
+ - Convert the payload string (e.g., session ID) to UTF-8 bytes
+ - Compute SHA-256 hash of the bytes (produces 32-byte hash)
+
+3. **Decode Signature**:
+ - If base64: decode to get 64-byte raw signature
+ - If multibase (starts with 'z'): decode base58btc first, then check if DER format
+ - If DER format: parse to extract r and s values, pad to 32 bytes each, concatenate to 64 bytes
+
+4. **Verify**: Use ECDSA verify function with:
+ - Algorithm: ECDSA
+ - Curve: P-256 (secp256r1, prime256v1)
+ - Hash: SHA-256
+ - Public key: The imported key
+ - Signature: 64-byte raw format (r || s)
+ - Message: The 32-byte SHA-256 hash
+
+5. **Return Result**: If any certificate's public key successfully verifies the signature, verification succeeds.
+
+**Library Examples**:
+- **Python**: `cryptography.hazmat.primitives.asymmetric.ec.ECDSA` with `SHA256`
+- **Go**: `crypto/ecdsa` and `crypto/sha256` packages
+- **Java**: `java.security.Signature` with "SHA256withECDSA"
+- **Rust**: `p256` crate or `ring` crate
+- **Node.js**: `crypto.subtle` (Web Crypto API) or `node:crypto`
+- **C#**: `System.Security.Cryptography.ECDsa` class
+
+### Step 6: Return Result
+
+The verification function should return a result indicating whether the signature is valid:
+
+```typescript
+interface VerifySignatureResult {
+ valid: boolean;
+ error?: string;
+ publicKey?: string; // The public key that successfully verified
+}
+```
+
+## Complete Verification Flow Diagram
+
+```mermaid
+flowchart TD
+ Start([Platform Receives
Signed Session]) --> ValidateInput{Validate Input}
+ ValidateInput -->|Invalid| Error1[Return Error]
+ ValidateInput -->|Valid| Resolve[Resolve eVault URL
from Registry]
+ Resolve --> GetCerts[Get Key Binding
Certificates from eVault]
+ GetCerts --> GetJWKS[Get JWKS from
Registry]
+ GetJWKS --> LoopStart{For Each Certificate}
+ LoopStart --> VerifyJWT[Verify JWT Signature
with Registry JWKS]
+ VerifyJWT -->|Invalid| NextCert{More Certificates?}
+ VerifyJWT -->|Valid| ExtractKey[Extract Public Key
from JWT Payload]
+ ExtractKey --> DecodeKey[Decode Multibase
Public Key]
+ DecodeKey --> ImportKey[Import Public Key
to Crypto Library]
+ ImportKey --> VerifySig[Verify Signature
ECDSA P-256, SHA-256]
+ VerifySig -->|Valid| Success([Return Success
with Public Key])
+ VerifySig -->|Invalid| NextCert
+ NextCert -->|Yes| LoopStart
+ NextCert -->|No| Fail([Return Failure])
+
+ style Success fill:#d4edda,color:#000000
+ style Fail fill:#f8d7da,color:#000000
+ style Error1 fill:#f8d7da,color:#000000
+```
+
+## Using the Signature Validator
+
+If you're using the TypeScript `signature-validator` package, verification is simplified:
+
+```typescript
+import { verifySignature } from "signature-validator";
+
+const verificationResult = await verifySignature({
+ eName: "@user-a.w3id",
+ signature: "xK3vJZQ2F3k5L8mN9pQrS7tUvW1xY3zA5bC7dE9fG1hIjKlMnOpQrStUvWxYz==",
+ payload: "550e8400-e29b-41d4-a716-446655440000",
+ registryBaseUrl: "https://registry.example.com"
+});
+
+if (verificationResult.valid) {
+ // Signature is valid
+ console.log("Verified with public key:", verificationResult.publicKey);
+} else {
+ // Signature is invalid
+ console.error("Verification failed:", verificationResult.error);
+}
+```
+
+## Signature Formats
+
+W3DS supports multiple signature formats:
+
+- **Software Keys**: Base64-encoded raw 64-byte signatures
+- **Hardware Keys**: Multibase base58btc-encoded signatures (starts with 'z')
+
+For detailed information on signature formats, encoding, and edge cases, see the [Signature Formats documentation](/docs/W3DS%20Protocol/Signature-Formats).
+
+## Library Requirements
+
+To implement signature verification, you'll need:
+
+- **HTTP client** for API requests (Registry and eVault)
+- **JWT library** for parsing and verifying JWTs (supports ECDSA P-256)
+- **ECDSA library** (most crypto libraries: OpenSSL, crypto in Node.js, cryptography in Python, etc.)
+- **Base64/base58btc decoder** for signature and public key decoding
+- **SHA-256 hashing** (usually included in crypto libraries)
+- **ASN.1 parser** (for DER format keys, usually included in crypto libraries)
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Signature verification fails**
+ - Check that the payload matches exactly what was signed
+ - Verify the eName is correct
+ - Ensure the signature format is supported (base64 or multibase)
+ - Check that the public key exists in eVault
+
+2. **Key binding certificate not found**
+ - Verify the public key was synced to eVault
+ - Check that the Registry service is accessible
+ - Ensure the eName matches the one used during key sync
+
+3. **JWT verification fails**
+ - Verify the Registry JWKS endpoint is accessible
+ - Check that the certificate hasn't expired (1 hour validity)
+ - Ensure the Registry's public key is correctly configured
+
+4. **Public key import fails**
+ - Verify the public key format (multibase, hex, or DER SPKI)
+ - Check that the key is for ECDSA P-256 curve
+ - Ensure the key is properly decoded from multibase format
+
+5. **Signature format issues**
+ - Check if signature is DER format and convert to raw if needed
+ - Verify base64/multibase decoding is correct
+ - Ensure signature is exactly 64 bytes after decoding
+
+## References
+
+- [Authentication](/docs/W3DS%20Protocol/Authentication) - How authentication uses signatures
+- [Signature Formats](/docs/W3DS%20Protocol/Signature-Formats) - Detailed signature format documentation
+- [ECDSA Specification](https://tools.ietf.org/html/rfc6979) - ECDSA algorithm details
+- [JWT Specification](https://tools.ietf.org/html/rfc7519) - JSON Web Token format
+- [Web Crypto API](https://www.w3.org/TR/WebCryptoAPI/) - Browser cryptographic API