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

#TitleStatusDate
001Cloudflare® Workers as Execution PlatformAccepted2025-04
002SQLite-backed Durable Objects for IdempotencyAccepted2025-05
003Mesh of specialized Workers (now 11 surfaces)Accepted2025-06
004Dual-Layer Configuration (Env + KV)Accepted2025-06
005Multi-Provider AI Gateway with Fallback ChainAccepted2026-03
006Bun Workspaces for Monorepo ManagementAccepted2025-04
007Opt-in Tenant Isolation (TenantEnv)Accepted2026-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.

WorkerResponsibility
hooxGateway, validation, rate limiting, idempotency, operator /wallet proxy
trade-workerCEX REST (Binance / Bybit / MEXC) + spot DEX via WEB3_WALLET_SERVICE
agent-workerAI risk management, trailing stops, cron jobs
d1-workerAll SQLite read/write operations
telegram-workerPush notifications, chat command handling
web3-wallet-workerOn-chain signing; Uniswap V3 + Jupiter Swap V2
email-workerEmail signal parsing (Mailgun / Email Routing)
analytics-workerMetrics collection (Analytics Engine writes)
report-workerPDF portfolio report generation
pyne-workerPine Script™ edge evaluate (Python tooling isolate)
dashboardNext.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:

  1. Build-time (.env.local + Workers Secrets):

    API keys, internal auth keys, dashboard credentials. Managed via hoox secrets set and hoox config env.

  2. Runtime (Cloudflare® KV): Kill switch, exchange routing, rate-limiter thresholds, email regex patterns. Managed via hoox config kv set and 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-manifest for 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:

text
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.lockb lockfile 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 prerequisites validation; 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 tenantId as untrusted
  • Mitigation: Lenient validation on message-carried ids (never throw); env tenant is authoritative when the flag is on