Deus - Service Entrypoints, API Server, and Schema Migrations
The Deus daemon (deusd), HTTP server assembly, developer SIWE authentication, registry and discovery route handlers, and the Postgres migration runner.
Overview
This section covers the Deus control plane: the daemon entrypoint (deusd), the HTTP server built on chi router, developer Sign-In-With-Ethereum (SIWE) authentication, the route handlers for registry, discovery, invocation, hosting, and dashboard flows, and the forward-only Postgres migration system.
Source version: 0.1.0-phase6
HTTP Server Assembly
Source: deus/internal/server/server.go
The server uses go-chi/chi/v5 with standard middleware: RequestID, RealIP, Recoverer, and Timeout(60s).
Deps
The server is constructed from a Deps struct that holds all long-lived service dependencies:
| Property | Type | Purpose |
|---|---|---|
Log | zerolog.Logger | Shared structured logger |
Store | *store.Store | Postgres-backed persistence |
Chain | *chain.Client | Paxeer chain 125 RPC client |
Registry | *registry.Service | Listing create/publish orchestrator |
Discovery | *discovery.Service | Semantic + lexical search |
Gateway | *gateway.Gateway | Quote + invoke + LXP payment gateway |
Hosting | *hosting.Orchestrator | Artifact upload + Appwrite deploy |
BlobURL | func(string) string | Object-store URL resolver |
DevMode | bool | Toggles dev-only header fallbacks |
PublishPrivateKey | string | Dev-mode on-chain publish key |
DeveloperAuthSecret | string | Keys SIWE nonces + developer tokens |
SIWEDomain | string | Optional EIP-4361 domain pin |
Route groups
| Group | Mount function | Auth |
|---|---|---|
| Health/metrics | Direct on router | None |
| Developer auth | mountDeveloperAuthRoutes | None (public) |
| Registry | mountRegistryRoutes | Owner routes require developer auth |
| Discovery | mountDiscoveryRoutes | None (public) |
| Invoke | mountInvokeRoutes | Agent bearer auth |
| Dashboard | mountDashboardRoutes | Mixed (some public, some owner-scoped) |
Health check
GET /internal/healthz pings Postgres and the chain client. Only Postgres health affects the ok field; a chain outage still returns HTTP 200 if Postgres is healthy.
Error Envelope
Source: deus/internal/server/errors.go
type APIError struct {
Error string `json:"error"`
Message string `json:"message"`
Detail map[string]any `json:"detail,omitempty"`
}
writeAPIError sets Content-Type: application/json, writes the status code, and encodes the APIError.
Developer Authentication (SIWE)
Source: deus/internal/server/devauth.go
Owner-scoped routes (service create, publish, pause, delist, deploy, analytics, earnings) use Sign-In-With-Ethereum (EIP-4361):
POST /v1/developers/noncereturns a stateless HMAC-bound nonce (5-minute TTL)POST /v1/developers/authverifies the SIWE message +personal_signsignature, recovers the wallet, and mints a 24-hour HMAC-bound developer token- The token travels as
X-Developer-Tokenon owner routes
Bare X-Developer-Wallet / X-Developer-Address headers are accepted only when DEUS_DEV=1.
Key parameters
| Parameter | Value |
|---|---|
| Nonce TTL | 5 minutes |
| Token TTL | 24 hours |
| Max SIWE message | 8192 bytes |
| Max auth body | 16 KB |
Nonces are stateless (random || expiry, HMAC-signed). MetaMask-style v values in {27,28} are normalized before recovery.
Registry Routes
Source: deus/internal/server/handlers_registry.go
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/services/ | Developer | Create a draft service from manifest JSON |
| GET | /v1/services/{id} | None | Get service by id or slug |
| POST | /v1/services/{id}/publish | Developer | Publish to Paxeer chain 125 |
| POST | /v1/services/{id}/pause | Developer (owner) | Pause listing |
| POST | /v1/services/{id}/delist | Developer (owner) | Delist service |
| POST | /v1/services/{id}/artifacts | Developer (owner) | Upload deployment artifact (multipart) |
| POST | /v1/services/{id}/deploy | Developer (owner) | Deploy to Paxeer Cloud |
| POST | /v1/services/{id}/redeploy | Developer (owner) | Redeploy service |
| GET | /v1/services/{id}/deployments | Developer (owner) | List deployments |
| GET | /v1/services/{id}/deployments/{did} | Developer (owner) | Get deployment |
| GET | /v1/services/{id}/logs | Developer (owner) | Recent invocation logs |
| GET | /v1/services/{id}/analytics | Developer (owner) | Usage analytics (30-day series, top operations) |
The resolveServiceID helper maps slugs to UUIDs so every /{id} route accepts both forms.
Discovery Routes
Source: deus/internal/server/handlers_discovery.go
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/discover?query=&kind=&limit= | None | Semantic + lexical search |
| POST | /v1/discover | None | Structured discovery with filters |
| GET | /v1/catalog?limit=&offset= | None | Public paginated catalog |
The catalog enriches each listing with headline pricing and tags from the stored manifest.
Invoke Routes
Source: deus/internal/server/handlers_invoke.go
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /v1/quote/{id} | Agent bearer | Build a signed quote (EIP-712) |
| POST | /v1/invoke/{id} | Agent bearer | Execute a service call |
| GET | /v1/invocations/{id} | Agent bearer | Get invocation result |
| GET | /v1/receipts/{id} | Agent bearer | Get signed receipt |
LXP (LayerX Payment) protocol
When the LXP rail is enabled, invoke follows a challenge-response pattern:
- Unpaid request returns HTTP 402 with an
lxp/1challenge (fresh nonce + pricing terms) - Caller retries with
X-LayerX-Paymentheader containing a signed payment intent - Successful response carries
X-LayerX-Receiptwith the payment receipt
Every payment failure returns a fresh 402 challenge. A rail outage returns 503 payment_unavailable.
Dashboard Routes
Source: deus/internal/server/handlers_dashboard.go
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/me | Agent or developer | Identity (DID, wallet, display name) |
| GET | /v1/me/spend | Agent bearer | Per-service spend breakdown |
| GET | /v1/me/services | Developer | Owned listings with usage aggregates |
| GET | /v1/me/earnings | Developer | Earnings (legacy wei + optional LayerX USDX) |
The earnings endpoint assembles a LayerXEarnings block when the LXP rail is active, joining deus invocation aggregates with a live LayerX account read for the developer's payee DID.
Postgres Store
Source: deus/internal/store/store.go
The store wraps a pgxpool.Pool and provides a forward-only migration runner:
- Reads
.sqlfiles from a directory in lexical order - Tracks applied versions in
schema_migrations - Each migration runs in its own transaction
- Requires
pgcryptoandpgvectorextensions
Migrations
| File | Purpose |
|---|---|
001_init.sql | Core tables: developers, services, endpoints, pricing_plans, embeddings, quotes, settlements, invocations, receipts, deployments, index_cursor |
002_draft_chain_id.sql | Make services.chain_id nullable for draft listings |
003_discovery_search.sql | Add tsvector search document + HNSW embedding index |
004_streams.sql | Payment stream tables |
005_settlement_vouchers.sql | Settlement voucher tables |
006_usdx_pricing.sql | USDX-denominated pricing columns |
007_lxp_invocations.sql | LXP invocation tracking |
008_developer_payee.sql | Developer payee DID column |
009_retire_rails.sql | Retire legacy payment rails |
