# not.bot™ Verify Web SDK Reference

## 1. Overview

The not.bot Verify Web SDK (`julia_web_sdk`) is a server-side library that runs in an application backend. It connects the backend to the not.bot Verify signature servers and handles the verification flow: starting signature requests, proxying the cryptographic exchange between the not.bot app and the operator's signature servers, and delivering results back to the application through callbacks.

The SDK is the final integration layer in the not.bot Verify stack. It sits between the operator's application backend and the signature servers deployed via the not.bot Verify Deployment Checklist. No user data or verification traffic ever reaches Julia Social. The entire exchange stays within the operator's infrastructure and the user's device.

**Repository:** `https://github.com/julia-social/julia_web_sdk`

**Version:** 1.0.0 (all languages)

**Languages:** Rust (reference implementation), JavaScript (Express), Python (FastAPI), Java (Spring MVC), Dart (shelf)

---

## 2. Repository Layout

```
julia_web_sdk/
├── rust/                    # Rust crate, reference implementation
│   ├── Cargo.toml
│   └── src/
│       ├── lib.rs           # ServiceBuilder + route handlers + WebSocket proxy
│       ├── signature_client.rs
│       ├── models.rs
│       ├── claims.rs
│       └── error.rs
├── javascript/              # JavaScript, Express adapter
│   ├── package.json
│   └── src/
│       ├── index.js         # Re-exports all public API
│       ├── models.js        # ClaimProperties enum + Bytes32/ByteArray validators
│       ├── client/
│       │   └── signatureClient.js   # SignatureClient + QR code generation
│       └── server/
│           └── expressAdapter.js    # Express router + WebSocket proxy
├── python/                  # Python, FastAPI adapter
│   ├── pyproject.toml
│   └── julia_web_sdk/
│       ├── __init__.py
│       ├── claims.py        # CLAIM_PROPERTIES dict
│       ├── models.py        # Pydantic models for all request/response types
│       ├── client.py         # SignatureClient + factory function
│       └── server_fastapi.py # FastAPISignatureAdapter + WebSocket proxy
├── java/                    # Java, Spring MVC adapter
│   ├── pom.xml
│   └── src/main/java/social/julia/sdk/
│       ├── ClaimProperties.java
│       ├── JuliaWebSdkException.java
│       ├── client/
│       │   └── SignatureClient.java
│       ├── model/
│       │   └── SignatureModels.java
│       └── server/
│           ├── SignatureAdapterConfig.java     # Builder for adapter configuration
│           ├── SpringSignatureController.java  # REST controller
│           ├── JuliaWebSocketConfig.java       # WebSocket registration
│           └── JuliaWebSocketProxyHandler.java # WS frame proxy
├── dart/                    # Dart, shelf adapter
│   ├── pubspec.yaml
│   └── lib/
│       ├── julia_web_sdk.dart
│       └── src/
│           ├── claims.dart
│           ├── models.dart
│           ├── signature_client.dart
│           └── shelf_adapter.dart
├── examples/                # Runnable examples per language
│   ├── javascript/
│   │   ├── express_server.mjs
│   │   └── client_usage.mjs
│   ├── python/
│   │   ├── fastapi_server.py
│   │   └── client_usage.py
│   ├── java/
│   │   ├── SpringSdkConfig.java
│   │   └── SignatureClientExample.java
│   ├── dart/
│   │   ├── shelf_server.dart
│   │   └── client_usage.dart
│   └── rust/
│       ├── server_integration.rs
│       └── client_usage.rs
└── shared/
    ├── api_contract.md       # Canonical endpoint contract (v1.0.0)
    └── claim_properties.txt  # All claim property URIs
```

Each language directory also contains a `README.md` and its lock file. The repository root holds `README.md` and `LICENSE`.

---

## 3. Two Integration Paths

### 3.1 Server Adapter (recommended)

Configure the adapter with verification requirements and callbacks; it mounts `/signature/*` routes into the web framework. The adapter handles session tracking, request-to-session mapping, and WebSocket proxying. The operator writes only the callbacks that decide what to do with verified results.

### 3.2 SignatureClient (lower-level)

An HTTP client that calls signature server endpoints with no adapter layer. Use it for manual control over the flow, automation scripts, or when the framework has no matching adapter.

---

## 4. Environment Variables

All languages read the same three environment variables:

| Variable | Default | Purpose |
|----------|---------|---------|
| `SIGNATURE_HOSTNAME` | `localhost` | Hostname of the load balancer in front of signature servers |
| `SIGNATURE_PORT` | `8080` | Port of the load balancer |
| `SIGNATURE_API_KEY` | `CHANGE_ME` | API key that authenticates the backend to signature servers |

The SDK uses HTTPS when `SIGNATURE_HOSTNAME` is anything other than `localhost`.

---

## 5. Adapter Configuration

All language adapters accept the same six configuration options:

| Option | Type | Default | Purpose |
|--------|------|---------|---------|
| `requestedClaims` | list of claim property URIs | `[]` | Credentials to request from the user (see Section 9) |
| `requireSitePass` | boolean | `false` | When true, generates a per-user-per-site identifier |
| `messageGenerator` | function → string | `() => ""` | Text the user sees in the not.bot app when approving |
| `onSuccess` | callback(verifyResponse, session) | stores in session | Called when verification succeeds |
| `onFailure` | callback(error) | no-op | Called when verification fails |
| `expireTimeSeconds` | integer (seconds) | `3600` | How long a verification request stays valid |

Option names appear here in JavaScript form. Python and Rust use snake_case equivalents: `requested_claims`, `require_site_pass`, `message_generator`, `on_success`, `on_failure`, and `expire_time_seconds` (Rust: `expire_time`).

### 5.1 onSuccess Callback

The `onSuccess` callback receives two arguments:

1. **verifyResponse**: contains the user's alias DID, any requested claims, the site pass (if requested), a timestamp, and the cryptographic presentation.
2. **session**: the originating user's session, looked up via the request-to-session mapping. The adapter resolves this even when the verification response arrives on a different HTTP connection (the not.bot app is a separate client).

### 5.2 Session Behavior

The adapter maps each `request_id` to the `session_id` that initiated it. This mapping lives in memory in the adapter process. If running multiple backend instances behind a load balancer, sticky sessions or a shared session store are required so the verification response routes back to the adapter instance holding the mapping.

---

## 6. SDK-Exposed Routes (Adapter Routes)

Once mounted, the adapter serves these routes from the operator's backend:

### HTTP Routes

| Route | Method | Called By | Purpose |
|-------|--------|-----------|---------|
| `/signature/notbot` | GET | Frontend | Starts a verification request. Returns a `request_id`. Frontend uses this to build the URL or QR code. |
| `/signature/status` | GET | Frontend | Returns `true` if the current session has completed verification. Frontend polls this to update UI. |
| `/signature/notbot/{request_id}` | POST | not.bot app | Accepts `{ "nonce": "<Bytes32>" }`. Adapter proxies to upstream, returns `{ "compressed_presentation": [...] }`. |
| `/signature/verify/{request_id}` | POST | not.bot app | Accepts `{ "presentation": [...] }`. Adapter proxies verification to upstream, fires `onSuccess` or `onFailure`. Returns `204` on success. |

### WebSocket Routes

| Route | Header Forwarded | Purpose |
|-------|-----------------|---------|
| `/signature/honestbot` | `x-presentation-hash` | Proxied to upstream signature server for honest.bot™ credential verification |
| `/calculate_site_pass` | `x-site-pass` | Proxied to upstream signature server for site pass computation |

**WebSocket proxy safety:** The adapter's upstream base URL must target the external signature service, not the same host serving SDK adapter routes. Pointing both to the same `/signature/*` host creates proxy-to-self loops on `/signature/honestbot`.

---

## 7. Upstream Signature Server Endpoints (Called by SDK)

These are the endpoints the SDK calls on the signature servers (via the load balancer):

| Endpoint | Method | Request Body | Response |
|----------|--------|-------------|----------|
| `/signature/start` | POST | `StartSignatureRequest` (see 7.1) | `{ "request_id": "..." }` |
| `/signature/presentation` | POST | `{ "request_id": "...", "nonce": "<Bytes32>" }` | `{ "compressed_presentation": [...] }` |
| `/signature/verify` | POST | `{ "request_id": "...", "presentation": [...] }` | `VerifySignatureResponse` (see 7.2) |
| `/signature/honestbot` | WS | n/a | Binary/text frame proxy |
| `/calculate_site_pass` | WS | n/a | Binary/text frame proxy |

All upstream HTTP calls include the `api-key` header with the value from `SIGNATURE_API_KEY`.

### 7.1 StartSignatureRequest

```json
{
  "requested_credentials": ["julia://./v1/pii/age_over_18"],
  "require_site_pass": true,
  "required_alias_launcher": null,
  "requested_message": [86, 101, 114, 105, 102, 121],
  "expires": 1730000000
}
```

Fields:
- `requested_credentials`: list of claim property URI strings
- `require_site_pass`: boolean
- `required_alias_launcher`: optional Bytes32 (null for most flows)
- `requested_message`: UTF-8 bytes of the message the user sees in the not.bot app
- `expires`: Unix UTC timestamp for request expiry

### 7.2 VerifySignatureResponse

```json
{
  "alias_did": {
    "launcher_id": [0, 1, 2, ...]
  },
  "site_pass": "0x00112233...",
  "claims": [
    {
      "property": "julia://./v1/pii/age_over_18",
      "value": [1]
    }
  ],
  "timestamp": 1730000000,
  "presentation": [1, 2, 3, ...]
}
```

Fields:
- `alias_did.launcher_id`: 32-byte array identifying the user's alias DID
- `site_pass`: Bytes32 hex string, present when `require_site_pass` was true
- `claims`: list of verified claims with property URI and byte-encoded value
- `timestamp`: Unix UTC timestamp of verification
- `presentation`: the full cryptographic presentation bytes

---

## 8. Data Encoding Conventions

| Type | Encoding | Example |
|------|----------|---------|
| `Bytes32` | 0x-prefixed, 64-char lowercase hex | `"0x00112233..."` |
| `Vec<u8>` / byte arrays | JSON array of integers 0–255 | `[86, 101, 114]` |
| Timestamps | Unix UTC seconds | `1730000000` |

The byte-array encoding matches Rust `serde` defaults and is consistent across all language implementations.

---

## 9. Claim Properties

Claims are identified by URI strings. The full list is in `shared/claim_properties.txt`. Each claim property maps a short name to a URI.

### 9.1 Bot Detection

| Name | URI | Category |
|------|-----|----------|
| `Notbot0` | `notbot://./v1/notbot0` | Basic bot detection |
| `Notbot1` | `notbot://./v1/notbot1` | Enhanced bot detection |
| `Notbot2` | `notbot://./v1/notbot2` | Highest bot detection |

### 9.2 Honest.bot & Licensing

| Name | URI | Category |
|------|-----|----------|
| `Honestbot0` | `notbot://./v1/honestbot0` | Honest.bot credential |
| `SignatureLicense` | `notbot://./v1/signature_license` | Signature license |

### 9.3 Site & Account

| Name | URI | Category |
|------|-----|----------|
| `SitePass` | `julia://./v1/site_pass` | Per-user-per-site pseudonym |
| `AccountName` | `sha2-256\|CBOR://./v1/account_name` | Account name (hashed) |
| `DomainName` | `sha2-256\|CBOR://./v1/domain_name` | Domain name (hashed) |

### 9.4 PII (Personal Identifiable Information)

| Name | URI |
|------|-----|
| `FirstName` | `julia://./v1/pii/first_name` |
| `GivenNames` | `julia://./v1/pii/given_names` |
| `FamilyName` | `julia://./v1/pii/family_name` |
| `Gender` | `julia://./v1/pii/gender` |
| `Nationality` | `julia://./v1/pii/nationality` |
| `BirthDate` | `julia://./v1/pii/date_of_birth` |
| `BirthDay` | `julia://./v1/pii/birth_day` |
| `BirthMonth` | `julia://./v1/pii/birth_month` |
| `BirthYear` | `julia://./v1/pii/birth_year` |

### 9.5 Age Verification

**Threshold claims:** `AgeOver13` through `AgeOver25`, plus `AgeOver100`. Each proves the user is at or above the specified age without revealing the actual age.

| Name | URI |
|------|-----|
| `Age` | `julia://./v1/pii/age` |
| `AgeOver13` | `julia://./v1/pii/age_over_13` |
| `AgeOver14` | `julia://./v1/pii/age_over_14` |
| `AgeOver15` | `julia://./v1/pii/age_over_15` |
| `AgeOver16` | `julia://./v1/pii/age_over_16` |
| `AgeOver17` | `julia://./v1/pii/age_over_17` |
| `AgeOver18` | `julia://./v1/pii/age_over_18` |
| `AgeOver19` | `julia://./v1/pii/age_over_19` |
| `AgeOver20` | `julia://./v1/pii/age_over_20` |
| `AgeOver21` | `julia://./v1/pii/age_over_21` |
| `AgeOver22` | `julia://./v1/pii/age_over_22` |
| `AgeOver23` | `julia://./v1/pii/age_over_23` |
| `AgeOver24` | `julia://./v1/pii/age_over_24` |
| `AgeOver25` | `julia://./v1/pii/age_over_25` |
| `AgeOver100` | `julia://./v1/pii/age_over_100` |

**Range claims:** Five-year and ten-year age brackets.

Five-year brackets: `AgeRange20To24`, `AgeRange25To29`, `AgeRange30To34`, `AgeRange35To39`, `AgeRange40To44`, `AgeRange45To49`, `AgeRange50To54`, `AgeRange55To59`, `AgeRange60To64`, `AgeRange65To69`, `AgeRange70To74`, `AgeRange75To79`, `AgeRange80To84`, `AgeRange85To89`, `AgeRange90To94`, `AgeRange95To99`.

Ten-year brackets: `AgeRange20To29`, `AgeRange30To39`, `AgeRange40To49`, `AgeRange50To59`, `AgeRange60To69`, `AgeRange70To79`, `AgeRange80To89`, `AgeRange90To99`.

All age range URIs follow the pattern `julia://./v1/pii/age_range_{low}_{high}`.

---

## 10. Verification Flow (Step by Step)

This is the complete flow as implemented in the SDK adapter code:

1. **Frontend calls `GET /signature/notbot`** on the SDK adapter.
2. The adapter clears any existing verification from the session.
3. The adapter calls **`POST /signature/start`** on the upstream signature server (via load balancer) with the configured claims, site pass requirement, message bytes, and expiry timestamp.
4. The signature server returns a `request_id`.
5. The adapter stores a **`request_id → session_id` mapping** in memory and returns the `request_id` to the frontend.
6. The frontend builds an **App Link URL** from the request_id: `https://not.bot/s1/{request_id}/{hostname}/{port}` and presents it as a tappable link (mobile) or QR code (desktop).
7. The **not.bot app** opens, connects to the SDK adapter endpoints, and begins the cryptographic exchange:
   - `POST /signature/notbot/{request_id}` with a nonce → adapter proxies to `POST /signature/presentation` upstream → returns `compressed_presentation`.
   - `POST /signature/verify/{request_id}` with the signed presentation → adapter proxies to `POST /signature/verify` upstream.
   - WebSocket connections on `/signature/honestbot` and `/calculate_site_pass` are proxied to upstream.
8. If upstream verification succeeds, the adapter looks up the originating session via the `request_id → session_id` mapping and calls **`onSuccess(verifyResponse, session)`**.
9. The frontend polls **`GET /signature/status`** to detect completion and update the UI.

### Network Path

During the verification exchange, traffic flows: user's phone → SDK adapter → signature servers. No packets reach Julia Social at any point. The entire exchange stays within the operator's infrastructure and the user's device.

---

## 11. Language-Specific Implementation Details

### 11.1 Rust

**Framework:** portfu (custom framework from GalactechsLLC)

**Key dependencies:**
- `dg_xch_core`: Chia blockchain primitives (`Bytes32`, BLS types)
- `portfu`: Web framework with macros, sessions, WebSocket support
- `reqwest`: HTTP client with cookie store
- `tokio`: Async runtime
- `time`: Timestamp handling

**Architecture:** The `ServiceBuilder` uses the builder pattern to configure claims, callbacks, and settings, then `.build()` converts it into a `ServiceGroup` (portfu's route group). Routes are defined with `#[get(...)]`, `#[post(...)]`, and `#[websocket(...)]` macros. Session state uses `Arc<RwLock<Session>>` with portfu's `SessionManager`.

**Notable implementation detail:** The Rust implementation uses `portfu::wrappers::sessions::Session` which stores typed data via `session.data.insert(response)` / `session.data.get::<VerifySignatureResponse>()`. The WebSocket proxy uses `tokio_tungstenite` and runs a polling loop that alternates reads between the client and upstream sockets.

**Error handling:** Uses a custom `Error` type with `ErrorCode` enum covering client errors (4xx range), server errors (5xx range), and Chia-specific errors (6xx range: CLVM, Garbler, Evaluator, BLS signature/credential/proof).

### 11.2 JavaScript

**Framework:** Express 5.x

**Key dependencies:**
- `express` ^5.1.0
- `express-session` ^1.18.2
- `qrcode` ^1.5.4 (QR code generation for `getSignatureRequestId()`)
- `ws` ^8.18.3 (WebSocket)

**Node.js requirement:** >=18

**Package type:** ES Module (`"type": "module"`)

**Exports:**
```javascript
import {
  ClaimProperties,             // Frozen object of all claim URIs
  createSignatureClient,       // Factory function → SignatureClient
  createExpressSignatureAdapter, // Factory → { router, attachWebsocketHandlers }
  SignatureClient,             // Class for direct endpoint calls
  JuliaWebSdkError,           // Custom error with status + body
  bytes32,                    // Validator for Bytes32 hex strings
  byteArray                   // Validator for byte arrays
} from "julia_web_sdk";
```

**Integration pattern:**
```javascript
const signatureAdapter = createExpressSignatureAdapter({
  signatureClient: createSignatureClient(),
  requestedClaims: [ClaimProperties.Notbot0, ClaimProperties.AgeOver18],
  requireSitePass: true,
  messageGenerator: () => "Verifying my identity with example.com",
  onSuccess: async (verifyResponse, session) => {
    session.juliaSignatureVerification = verifyResponse;
  },
  onFailure: async (error) => {
    console.error("verification failure", error);
  },
});

app.use(signatureAdapter.router);
signatureAdapter.attachWebsocketHandlers(httpServer);
```

**Session handling:** Uses `req.sessionID` and `req.sessionStore` for cross-connection session resolution. The adapter's `requestToSignatureSession` is an in-memory `Map`.

**WebSocket proxy:** Uses the `ws` library's `WebSocketServer` with `noServer: true`. Upgrades are intercepted on the HTTP server's `upgrade` event. The proxy forwards `x-presentation-hash` and `x-site-pass` headers.

**SignatureClient extras:** The JS client includes `getSignatureRequestId()` which calls `/signature/notbot`, builds the `https://not.bot/` App Link URL, generates a QR code via the `qrcode` library, and returns `{ request_id, qr_code, url }`.

### 11.3 Python

**Framework:** FastAPI

**Key dependencies:**
- `fastapi` >=0.115.0
- `httpx` >=0.27.0 (async HTTP client)
- `pydantic` >=2.8.0 (data models)
- `websockets` >=13.1 (WebSocket client)

**Python requirement:** >=3.10

**Exports:**
```python
from julia_web_sdk import (
    CLAIM_PROPERTIES,                  # Dict[str, str] of all claim URIs
    FastAPISignatureAdapter,          # Adapter class
    create_fastapi_router,            # Shorthand factory
    SignatureClient,                  # Direct HTTP client
    SignatureClientConfig,            # Dataclass for client config
    create_signature_client_from_env, # Factory from env vars
    JuliaWebSdkError,                # Custom exception
)
```

**Integration pattern:**
```python
from julia_web_sdk import FastAPISignatureAdapter, create_signature_client_from_env, CLAIM_PROPERTIES

adapter = FastAPISignatureAdapter(
    signature_client=create_signature_client_from_env(),
    requested_claims=[CLAIM_PROPERTIES["Notbot0"], CLAIM_PROPERTIES["AgeOver18"]],
    require_site_pass=True,
    message_generator=lambda: "Verifying my identity with example.com",
)

app.include_router(adapter.router)
```

**Session handling:** The Python adapter uses `session_cookie_name` (default: `"session"`) to read the session cookie value. The session-to-request mapping (`session_signatures`) is an in-memory dict. An optional `resolve_session` callback allows custom session resolution for cross-connection flows.

**Data models:** All request/response types are Pydantic v2 `BaseModel` subclasses: `StartSignatureRequest`, `StartSignatureResponse`, `GeneratePresentationRequest`, `GeneratePresentationResponse`, `VerifySignatureRequest`, `VerifySignatureResponse`, `Claim`, `DIDInfo`, `ServerPresentation`, `ClientPresentation`, `SignatureRequest`.

**WebSocket proxy:** Uses the `websockets` library (`websockets.connect`). Runs two async tasks (`from_client` and `from_upstream`) in parallel via `asyncio.wait(FIRST_COMPLETED)`, canceling the other when one completes.

### 11.4 Java

**Framework:** Spring MVC 6.x + Spring WebSocket

**Key dependencies:**
- `jackson-databind` 2.17.2
- `spring-webmvc` 6.1.12
- `spring-websocket` 6.1.12
- `jakarta.servlet-api` 6.0.0

**Java requirement:** 17+

**Architecture:** Four classes comprise the server adapter:
- `SignatureAdapterConfig`: Builder pattern for adapter configuration with functional interfaces `OnSuccess`, `OnFailure`, and `SessionResolver`.
- `SpringSignatureController`: `@RestController` with `@GetMapping`/`@PostMapping` handlers for the four HTTP routes.
- `JuliaWebSocketConfig`: `WebSocketConfigurer` that registers the two WebSocket proxy handlers.
- `JuliaWebSocketProxyHandler`: `AbstractWebSocketHandler` that proxies frames in both directions using `java.net.http.WebSocket`.

**Session handling:** Uses `ConcurrentHashMap<String, String>` for `request_id → session_id` mapping. The `SessionResolver` functional interface allows custom session lookup.

**SignatureClient:** Uses `java.net.http.HttpClient` with async `sendAsync()` returning `CompletableFuture`. JSON serialization via Jackson `ObjectMapper` with `PropertyNamingStrategies.SNAKE_CASE`.

**Integration pattern:**
```java
@Bean
public SignatureAdapterConfig signatureAdapterConfig() {
    return SignatureAdapterConfig.builder()
        .requestedClaims(List.of(ClaimProperties.NOTBOT0, ClaimProperties.AGE_OVER_18))
        .requireSitePass(true)
        .messageGenerator(() -> "Verifying my identity with example.com")
        .onSuccess((response, session) -> {
            session.setAttribute("juliaSignatureVerification", response);
        })
        .build();
}
```

### 11.5 Dart

**Framework:** shelf + shelf_router

**Key dependencies:**
- `http` ^1.2.2
- `shelf` ^1.4.1
- `shelf_router` ^1.1.4
- `shelf_web_socket` ^2.0.0
- `web_socket_channel` ^3.0.1

**Dart SDK requirement:** >=3.4.0 <4.0.0

**Architecture:** Follows the same pattern as other languages: `SignatureClient` for HTTP calls, `ShelfSignatureAdapter` for mounting routes. WebSocket proxying uses `web_socket_channel` for upstream connections and `shelf_web_socket` for incoming connections.

---

## 12. SignatureClient API (All Languages)

The `SignatureClient` provides both upstream (signature server) and SDK adapter endpoint methods.

### 12.1 Upstream Methods (require API key)

| Method | Calls | Returns |
|--------|-------|---------|
| `startSignature(request)` | `POST /signature/start` | `{ request_id }` |
| `generatePresentation(request)` | `POST /signature/presentation` | `{ compressed_presentation }` |
| `verifyPresentation(request)` | `POST /signature/verify` | `VerifySignatureResponse` |

### 12.2 SDK Adapter Methods (no API key, uses cookies/credentials)

| Method | Calls | Returns |
|--------|-------|---------|
| `getSignatureRequestId()` | `GET /signature/notbot` | request_id string (JS also returns QR + URL) |
| `getSignatureStatus()` | `GET /signature/status` | boolean |
| `generateSignaturePresentation(requestId, nonce)` | `POST /signature/notbot/{id}` | `{ compressed_presentation }` |
| `verifySignaturePresentation(requestId, presentation)` | `POST /signature/verify/{id}` | void (204) |

### 12.3 Constructor Options

| Option | Default | Purpose |
|--------|---------|---------|
| `baseUrl` / `host` + `port` | from env vars | Signature server address |
| `apiKey` | `SIGNATURE_API_KEY` env var | Authentication key |
| `timeout` | 180 seconds | Request timeout |
| `secure` | `true` unless localhost | Use HTTPS |

---

## 13. Error Handling

All language implementations throw/raise a custom error type when API calls fail:

| Language | Error Type | Properties |
|----------|-----------|------------|
| Rust | `error::Error` | `message: String`, `code: ErrorCode` |
| JavaScript | `JuliaWebSdkError` | `message`, `status`, `body` |
| Python | `JuliaWebSdkError` | `message`, `status_code`, `body` |
| Java | `JuliaWebSdkException` | `message`, `statusCode`, `body` |
| Dart | `JuliaWebSdkException` | `message`, `statusCode`, `body` |

The Rust `ErrorCode` enum is the most detailed: a retry band (300-301), client errors (400-499), server errors (500-518), and Chia/MPC/BLS errors (600-605), plus a catch-all `Other` (999).

### 13.1 Adapter Error Responses

| Scenario | HTTP Status | Handler |
|----------|-------------|---------|
| Upstream signature server unreachable | 502 | `onFailure` called |
| Missing nonce or presentation in request body | 400 | Direct error response |
| Upstream verification rejects the presentation | 422 | `onFailure` called |
| `onSuccess` callback throws | 500 | `onFailure` called |

---

## 14. Quick Start Per Language

### JavaScript
```bash
cd javascript && npm install
# Set env vars: SIGNATURE_HOSTNAME, SIGNATURE_PORT, SIGNATURE_API_KEY
node examples/javascript/express_server.mjs
```

### Python
```bash
cd python && pip install -e .
# Set env vars
uvicorn examples.python.fastapi_server:app
```

### Java
```bash
cd java && mvn -q package
# Integrate SignatureAdapterConfig + SpringSignatureController into Spring Boot app
```

### Dart
```bash
cd dart && dart pub get
dart run examples/dart/shelf_server.dart
```

### Rust
```bash
cd rust && cargo check
# Integrate ServiceBuilder into portfu application
```

---

## 15. Operational Considerations

### 15.1 Session Stickiness

The `request_id → session_id` mapping lives in memory. When running multiple backend instances behind a load balancer, either use sticky sessions or a shared session store. Without this, the verification response may arrive at a different adapter instance than the one holding the mapping, and the `onSuccess` callback will not find the correct session.

### 15.2 WebSocket Proxy Loops

Configure `SIGNATURE_HOSTNAME` to point at the signature server load balancer, not at the host running the SDK adapter. If both point at the same host, the WebSocket proxy will loop back to itself on `/signature/honestbot`.

### 15.3 Timeout

All language clients default to a 180-second timeout on all HTTP requests to the signature server. This accommodates the time a user may take to approve the verification in the not.bot app.

### 15.4 not.bot App Link Format

The JavaScript `SignatureClient` builds App Links in this format:

```
https://not.bot/s1/{request_id}/{hostname}/{port}
```

The `https://not.bot/` form is an App Link (iOS Universal Link / Android App Link). On mobile, it opens the not.bot app. On desktop, the URL renders as a QR code the user scans with a phone.

---

## 16. Relationship to Other Components

The SDK is the final layer in the not.bot Verify deployment stack:

```mermaid
flowchart TB
  frontend["Frontend (QR / Link, in user's browser)"]
  phone["User's phone: not.bot app<br/>(talks only to the operator's backend)"]

  subgraph infra["Operator's Infrastructure"]
    backend["Backend + SDK Adapter<br/>/signature/* routes<br/>(runs outside the cluster)"]

    subgraph cluster["Kubernetes cluster (private, no public ingress)"]
      siglb["Signature-server LB (internal)"]
      sig["Signature servers (load balanced)"]
      admin["Admin service<br/>(mints signature DID pool, issues the SDK API key)"]
      bao["OpenBao + Chiakeys (DID keys)"]
      chia["Chia nodes"]
    end
  end

  frontend <--> backend
  phone -->|"https://not.bot App Link"| backend
  backend -->|"proxies /signature/*"| siglb
  siglb --> sig
  sig <-->|"scoped token → DID keys"| bao
  sig <-->|"mTLS"| chia
  admin --> bao
  admin --> chia
  sig -.->|"registers, heartbeats, pulls config"| admin
```

For the full deployment topology, including PostgreSQL, Keycloak, the operator's edge, and the two hourly aggregate counts to Julia Social, see the Architecture and Privacy Guide §4.

The SDK depends on:
- **Signature servers** being deployed and healthy (Architecture and Privacy Guide §3, §6)
- **Signature DID pool** being minted (Architecture and Privacy Guide §6, "Scaling")
- **Admin service** running (Architecture and Privacy Guide §3)
- **Load balancer / ingress** configured to route to signature servers (Architecture and Privacy Guide §4)
- **API key** obtained from the admin interface (Deployment Checklist, Phase 9)

The SDK does NOT communicate with Julia Social. All traffic stays within the operator's infrastructure.
