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:
- verifyResponse: contains the user's alias DID, any requested claims, the site pass (if requested), a timestamp, and the cryptographic presentation.
- 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
{
"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 stringsrequire_site_pass: booleanrequired_alias_launcher: optional Bytes32 (null for most flows)requested_message: UTF-8 bytes of the message the user sees in the not.bot appexpires: Unix UTC timestamp for request expiry
7.2 VerifySignatureResponse
{
"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 DIDsite_pass: Bytes32 hex string, present whenrequire_site_passwas trueclaims: list of verified claims with property URI and byte-encoded valuetimestamp: Unix UTC timestamp of verificationpresentation: 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:
- Frontend calls
GET /signature/notboton the SDK adapter. - The adapter clears any existing verification from the session.
- The adapter calls
POST /signature/starton the upstream signature server (via load balancer) with the configured claims, site pass requirement, message bytes, and expiry timestamp. - The signature server returns a
request_id. - The adapter stores a
request_id → session_idmapping in memory and returns therequest_idto the frontend. - 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). - 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 toPOST /signature/presentationupstream → returnscompressed_presentation.POST /signature/verify/{request_id}with the signed presentation → adapter proxies toPOST /signature/verifyupstream.- WebSocket connections on
/signature/honestbotand/calculate_site_passare proxied to upstream.
- If upstream verification succeeds, the adapter looks up the originating session via the
request_id → session_idmapping and callsonSuccess(verifyResponse, session). - The frontend polls
GET /signature/statusto 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 supportreqwest: HTTP client with cookie storetokio: Async runtimetime: 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.0express-session^1.18.2qrcode^1.5.4 (QR code generation forgetSignatureRequestId())ws^8.18.3 (WebSocket)
Node.js requirement: >=18
Package type: ES Module ("type": "module")
Exports:
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:
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.0httpx>=0.27.0 (async HTTP client)pydantic>=2.8.0 (data models)websockets>=13.1 (WebSocket client)
Python requirement: >=3.10
Exports:
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:
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-databind2.17.2spring-webmvc6.1.12spring-websocket6.1.12jakarta.servlet-api6.0.0
Java requirement: 17+
Architecture: Four classes comprise the server adapter:
SignatureAdapterConfig: Builder pattern for adapter configuration with functional interfacesOnSuccess,OnFailure, andSessionResolver.SpringSignatureController:@RestControllerwith@GetMapping/@PostMappinghandlers for the four HTTP routes.JuliaWebSocketConfig:WebSocketConfigurerthat registers the two WebSocket proxy handlers.JuliaWebSocketProxyHandler:AbstractWebSocketHandlerthat proxies frames in both directions usingjava.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:
@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.2shelf^1.4.1shelf_router^1.1.4shelf_web_socket^2.0.0web_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
cd javascript && npm install
# Set env vars: SIGNATURE_HOSTNAME, SIGNATURE_PORT, SIGNATURE_API_KEY
node examples/javascript/express_server.mjs
Python
cd python && pip install -e .
# Set env vars
uvicorn examples.python.fastapi_server:app
Java
cd java && mvn -q package
# Integrate SignatureAdapterConfig + SpringSignatureController into Spring Boot app
Dart
cd dart && dart pub get
dart run examples/dart/shelf_server.dart
Rust
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:
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.