adrs
This page
{/* Copyright (c) 2026 HOOX · jango-blockchained (hoox-sh) SPDX-License-Identifier: CC-BY-4.0 */}
This directory contains the Architecture Decision Records for the Hoox trading platform. Each ADR documents a significant architectural choice, its context, the decision made, and its consequences.
ADR Index
| # | Title | Status | Date |
|---|---|---|---|
| 001 | Cloudflare® Workers as Execution Platform | Accepted | 2025-04 |
| 002 | SQLite-backed Durable Objects for Idempotency | Accepted | 2025-05 |
| 003 | Mesh of specialized Workers (now 11 surfaces) | Accepted | 2025-06 |
| 004 | Dual-Layer Configuration (Env + KV) | Accepted | 2025-06 |
| 005 | Multi-Provider AI Gateway with Fallback Chain | Accepted | 2026-03 |
| 006 | Bun Workspaces for Monorepo Management | Accepted | 2025-04 |
| 007 | Opt-in Tenant Isolation (TenantEnv) | Accepted | 2026-10 |
ADR-001: Cloudflare® Workers as Execution Platform
Status: Accepted
Context: Algorithmic trading requires ultra-low latency between signal generation and order execution. Traditional VPS-based bots suffer from 200ms+ round-trip latency due to fixed geographic locations and heavy runtime overhead.
Decision: Deploy all trading logic as Cloudflare® Workers running on V8 isolates at the edge.
Consequences:
- Positive: 30-60% latency reduction vs. VPS
- Negative: 128MB memory limit per isolate
- Mitigation: Web3 wallet worker uses EVM-compatible RPC calls
ADR-002: SQLite-backed Durable Objects for Idempotency
Status: Accepted (evolved v0.13: two-phase + fail-closed)
Context: Webhook-based trade execution is inherently unreliable — network timeouts can cause TradingView® to retry the same signal, potentially resulting in duplicate trades. Gateways need at-most-once acceptance within a TTL without permanently blocking legitimate retries after soft failures.
Decision: Implement idempotency using Cloudflare® Durable Objects with SQLite-backed state (IdempotencyStore). Keys prefer client idempotencyKey / Idempotency-Key; else a composite fingerprint with live/test mode and a per-minute time bucket. Flow is two-phase: reserve (pending) before queue/service work, commit only on true success, release on soft-fail or error. Missing DO binding is fail-closed. A separate RateLimiterStore DO provides atomic multi-isolate rate limits.
Consequences:
- Positive: Single-threaded DO isolation; in-flight retries blocked; failed trades can retry after release
- Negative: Extra DO RPCs per request; fail-closed can refuse trades if DO misconfigured
- Mitigation: Sharded object names; TTL alarms for GC; deploy checklist for migrations
v1/v2
ADR-003: Mesh of specialized Workers
Status: Accepted (evolved: 11 compute surfaces as of 0.15.0; unchanged in 0.16.0)
Context: Monolithic trading systems are fragile — a crash in any component (portfolio calculation, notification, database) takes down the entire system. Microservices provide isolation but increase operational complexity.
Decision: Split the platform into specialized Workers, each with a single responsibility. The original nine isolates remain; pyne-worker (Python Pine evaluate) and the dashboard (Next.js 16 / OpenNext) bring the mesh to 11 compute surfaces.
| Worker | Responsibility |
|---|---|
hoox | Gateway, validation, rate limiting, idempotency, operator /wallet proxy |
trade-worker | CEX REST (Binance / Bybit / MEXC) + spot DEX via WEB3_WALLET_SERVICE |
agent-worker | AI risk management, trailing stops, cron jobs |
d1-worker | All SQLite read/write operations |
telegram-worker | Push notifications, chat command handling |
web3-wallet-worker | On-chain signing; Uniswap V3 + Jupiter Swap V2 |
email-worker | Email signal parsing (Mailgun / Email Routing) |
analytics-worker | Metrics collection (Analytics Engine writes) |
report-worker | PDF portfolio report generation |
pyne-worker | Pine Script™ edge evaluate (Python tooling isolate) |
dashboard | Next.js ops console (public) |
Consequences:
- Positive: Independent scaling; failure isolation; focused development; clean ownership boundaries
- Negative: More deployment units to manage; Service Binding dependency chain complexity; inter-worker communication overhead
- Mitigation: CLI automates dependency-ordered deployment; health check endpoints on every worker; TUI shows mesh status
ADR-004: Dual-Layer Configuration (Environment + KV)
Status: Accepted
Context: Configuration needs fall into two categories.
Decision: Use two distinct configuration layers:
Build-time (
.env.local+ Workers Secrets):API keys, internal auth keys, dashboard credentials. Managed via
hoox secrets setandhoox config env.Runtime (Cloudflare® KV): Kill switch, exchange routing, rate-limiter thresholds, email regex patterns. Managed via
hoox config kv setand propagates globally in under 10 seconds.
Consequences:
- Positive: Kill switch takes effect on next request
- Negative: Config values are eventually consistent
- Mitigation: KV manifest with documented defaults;
hoox config kv apply-manifestfor one-command sync; documentation clearly separates the two layers
ADR-005: Multi-Provider AI Gateway with Fallback Chain
Status: Accepted
Context: The agent-worker needs LLM capabilities for risk assessment, market analysis, and user chat. Single-provider AI introduces single points of failure and cost concentration.
Decision: Implement a 5-provider fallback chain ordered by cost and availability:
Workers AI (free) -> Anthropic -> Google AI -> OpenAI -> Azure OpenAI
Each provider is health-checked before use. On failure, the gateway automatically retries with the next provider. Usage and costs are tracked per-provider.
Consequences:
- Positive: Zero single point of failure for AI; cost optimization (use free tier first); resilience against provider outages; vision + reasoning + chat in one gateway
- Negative: Complex provider abstraction layer; potential for inconsistent response quality across providers; harder to optimize prompts for all providers simultaneously
- Mitigation: Provider selection per-request (caller specifies preferred provider); standardized response schemas; usage dashboard for cost monitoring
ADR-006: Bun Workspaces for Monorepo Management
Status: Accepted
Context: The project contains multiple packages (CLI, TUI, shared library, 11 compute surfaces) that need to share types, utilities, and be tested/developed locally.
Decision: Use Bun Workspaces as the monorepo manager instead of npm/yarn/pnpm workspaces. Rationale:
- Native TypeScript compilation (no separate tsconfig complexity)
- Built-in test runner (no Jest/Mocha dependency)
- Fastest install times among JS package managers
- Single
bun.lockblockfile for all workspaces - Native script runner for CI pipeline
Consequences:
- Positive: Zero-config TypeScript; fast CI; unified tooling; shared types resolve at compile time
- Negative: Bun is less mature than npm ecosystem; some packages may have compatibility issues; harder to onboard developers unfamiliar with Bun
- Mitigation: Comprehensive
hoox check prerequisitesvalidation; Docker runtime as fallback; documentation covers both native and Docker workflows
ADR-007: Opt-in Tenant Isolation (TenantEnv)
Status: Accepted (shipped 0.14; off by default)
Context: Self-hosted installs are single-tenant. Platform deploys need namespaced KV, D1, R2, and Durable Object keys without changing the default self-host path.
Decision: Export TenantEnv / TenantScope from @hoox-sh/hoox-shared/tenant. Isolation is a no-op unless TENANT_MODE is "true" or "1" and TENANT_ID matches [A-Za-z0-9_-]{1,64}. Setting TENANT_ID alone does not enable scoping. When on, helpers namespace KV/DO keys (t/<id>/…), R2 keys (t-<id>/…), stamp D1 tenant_id, and attach tenantId on outbound payloads.
Entitlement types (EntitlementScope, hasScope()) classify bearers only. They do not enforce product entitlements on the request path.
Consequences:
- Positive: Existing single-tenant installs unchanged; fail-closed when the flag is on without a valid id
- Negative: Shared queue consumers must treat message-carried
tenantIdas untrusted - Mitigation: Lenient validation on message-carried ids (never throw); env tenant is authoritative when the flag is on