This document describes the architecture of the Akash Console monorepo: its applications, data stores, external integrations, and how everything is deployed. For a high-level overview and the main system diagram, see the README.
Akash Console is a monorepo managed with npm workspaces. It powers the managed Console at console.akash.network, where users deploy Docker workloads on the Akash Network using a managed wallet. The self-custody variant (Keplr / your own wallet) lives in a separate repository, Akash Console Air.
The system is split into three frontends, six backend services, and a set of shared packages. Backend Node services use tsyringe for dependency injection, except notifications (NestJS modules) and indexer (plain procedural code). Frontends are Next.js. All services are written in strict TypeScript and shipped as Docker images.
- deploy-web (
apps/deploy-web) - the flagship Console UI for building SDLs, deploying, and managing deployments. Talks toapiover REST and toprovider-proxyover REST + WebSocket (for manifest send, log/shell/event streaming). Serves console.akash.network. No direct database access. - stats-web (
apps/stats-web) - the public network-statistics site, stats.akash.network. Renders charts of network usage, providers, leases, and market data, all fetched fromapi. - provider-console (
apps/provider-console) - a wizard UI for creating and managing an Akash provider, provider-console.akash.network. Its primary backend is a separate Go service (provider-console-api/provider-console-security) that lives outside this monorepo; it calls the Consoleapionly secondarily.
- api (
apps/api) - the central REST/OpenAPI backend behind console-api.akash.network. Built with Hono +@hono/zod-openapi+ tsyringe. It serves deployments, billing, certificates, dashboard/stats aggregation, templates, providers, bid-screening proxy, notifications proxy, and confidential-compute attestation. It forks worker-thread "interfaces":rest(HTTP, port 3080) andbackground-jobs(pg-boss queue + cron, port 3081). It is the only service that spans both databases: it reads the chain DB (Sequelize) and reads/writes the user DB (Drizzle + pg-boss). - indexer (
apps/indexer) - downloads Akash chain blocks and indexes them into the chain DB. Plain Node + Hono (status endpoint on port 3079) driven by a custom scheduler. See Indexer pipeline below. - notifications (
apps/notifications) - a NestJS service that listens to chain events, evaluates alerts, and sends email via Novu. Uses pg-boss as its message broker. The runtime interface is selected via theINTERFACEenv var (rest,chain-events,alert-events,notifications-events, orall). Reached throughapi, which proxies/notificationsroutes to it. - provider-proxy (
apps/provider-proxy) - bridges browser requests to Akash providers, which speak mTLS and cannot be reached directly from a browser. Hono + a barewsWebSocket server on port 3040. Also validates certificates against a chain REST node. Serves console-provider-proxy.akash.network. - provider-inventory (
apps/provider-inventory) - the bid-screening service. It keeps a live table of each provider's cluster capacity and pre-filters providers that could satisfy a deployment's GroupSpec. Hono + tsyringe + Drizzle. It forks two worker-thread interfaces:rest(port 3092, exposes/v1/bid-screening) andproviders-sync(port 3093, the streamer/discovery scheduler that opens long-lived gRPCstreamStatusconnections to providers). Reached only throughapi. Seeapps/provider-inventory/CONTEXT.mdfor the full domain model. - tx-signer (
apps/tx-signer) - an internal service that signs and broadcasts Akash transactions with the funding wallet or a derived wallet (the managed-wallet flow). Hono + tsyringe. Endpoints:POST /v1/tx/funding,POST /v1/tx/derived. Called byapi; no database.
- log-collector (
apps/log-collector) - collects container logs and Kubernetes events from all pods in a namespace and forwards them to Datadog via Fluent Bit. Deployed onto Akash provider clusters rather than as part of the Console platform. Image:ghcr.io/akash-network/log-collector.
All databases are PostgreSQL. In development they run as five logical databases on a single Postgres instance (seeded from the POSTGRES_DBS list in packages/docker/.env.sandbox.docker-compose-dev); in production they are split across managed instances.
| Database | Owner (writes) | Read by | ORM |
|---|---|---|---|
console-users |
api | api | Drizzle (also hosts the pgboss job-queue schema) |
console-akash-sandbox (chain / indexer DB) |
indexer | api, and stats-web via api | Sequelize |
notification_service |
notifications | notifications | Drizzle |
events |
notifications | notifications | pg-boss (message broker) |
provider_inventory |
provider-inventory | provider-inventory | Drizzle |
Notes:
- Two ORMs coexist in
api: Sequelize for reading the chain DB, Drizzle for the user DB. Shared chain schemas live inpackages/databaseand are consumed by the indexer. - No Redis / BullMQ / RabbitMQ. Both the
apibackground-job queue and thenotificationsbroker are backed by pg-boss (Postgres). - The indexer also keeps an on-disk LevelDB cache for raw block JSON during syncing.
For table-level detail, see database-structure.md.
A typical managed-wallet deployment flows through the platform like this:
%%{init: {'theme':'base','themeVariables':{'fontFamily':'Geist, Inter, system-ui, sans-serif','background':'#171717','actorBkg':'#3a1a1d','actorBorder':'#ff5a63','actorTextColor':'#ffb3b7','actorLineColor':'#5a5650','signalColor':'#a8a29a','signalTextColor':'#d9d5cd','noteBkg':'#362013','noteBorder':'#ef9a5c','noteTextColor':'#f3c39a','labelBoxBkgColor':'#242422','labelBoxBorderColor':'#45423c','labelTextColor':'#e7e3da','activationBkgColor':'#242422','activationBorderColor':'#4fce74'}}}%%
sequenceDiagram
actor U as User
participant DW as deploy-web
participant API as api
participant PI as provider-inventory
participant TXS as tx-signer
participant CH as Akash chain
participant PP as provider-proxy
participant PR as Provider
U->>DW: build SDL, deploy
DW->>API: POST /v1/... (Auth0 JWT)
API->>PI: screen candidate providers
API->>TXS: sign + broadcast (managed wallet)
TXS->>CH: MsgCreateDeployment
CH-->>API: bids
U->>DW: accept bid, create lease
API->>TXS: sign + broadcast MsgCreateLease
DW->>PP: send manifest, stream logs (REST + WS)
PP->>PR: mTLS to provider
Billing runs alongside this flow through Stripe (handled in api), and deployment lifecycle events are picked up by notifications, which emails users via Novu.
The indexer is scheduler-driven. Its core "Sync Blocks" task runs every 7 seconds as a three-stage pipeline, alongside periodic tasks (sync providers, price history, provider uptime, IP lookup, keybase info):
%%{init: {'theme':'base','themeVariables':{'fontFamily':'Geist, Inter, system-ui, sans-serif','fontSize':'14px','background':'#171717','primaryColor':'#242422','primaryTextColor':'#e7e3da','primaryBorderColor':'#45423c','lineColor':'#8f8f8f','textColor':'#d9d5cd','clusterBkg':'#1a1a1a','clusterBorder':'#3a3a3a','titleColor':'#f5f2ec','edgeLabelBackground':'#1a1a1a'},'flowchart':{'curve':'basis','nodeSpacing':45,'rankSpacing':60}}}%%
flowchart LR
classDef chain fill:#1c1c1c,stroke:#5a5650,color:#d9d5cd;
classDef svc fill:#262421,stroke:#47433c,color:#d9d3c8;
RPC["Akash RPC nodes"]:::chain -->|"download blocks + results"| DL["Download<br/>nodeAccessor · concurrency 100"]:::svc
DL --> CACHE[("LevelDB<br/>on-disk cache")]:::svc
CACHE --> INS["Insert<br/>decode txs into Block/Tx/Event/Message<br/>Sequelize · 50-block transactions"]:::svc
INS --> DB[("chain / indexer DB")]:::svc
INS --> PROC["Process / Index<br/>akashStats · bme · validator · messageAddresses"]:::svc
PROC --> DB
- Download -
downloadBlocks()fetches blocks and block-results from RPC nodes (concurrency 100, batched to 1,000 blocks per pass), caching raw JSON in LevelDB. - Insert -
insertBlocks()decodes transactions with cosmjs, builds Block / Transaction / TransactionEvent / Message rows plus raw BME events, and bulk-inserts in Sequelize transactions of 50 blocks. - Process / Index -
statsProcessor.processMessages()runs the registered indexers (akashStatsIndexer,bmeIndexer,validatorIndexer,messageAddressesIndexer) to derive deployments, leases, bids, provider snapshots, and the BME ledger. The LevelDB cache is then cleared unlessKEEP_CACHEis set.
| System | Used by | Purpose |
|---|---|---|
| Akash chain nodes (RPC / REST) | indexer, api, notifications, tx-signer, provider-proxy | Block download, chain reads, tx broadcast, event listening, certificate validation |
| Akash providers | provider-proxy, provider-inventory | mTLS REST/WS bridge; gRPC streamStatus for capacity |
| Auth0 | api, deploy-web | Authentication (M2M + user JWT); dev uses a mock OAuth server |
| Stripe | api, deploy-web | Payments / billing and webhooks |
| Novu | notifications | Email delivery |
| Amplitude | api, notifications, deploy-web | Product analytics |
| Google Analytics / GTM | deploy-web | Web analytics |
| Unleash | api, deploy-web, stats-web | Feature flags |
| Cloudflare Turnstile | deploy-web | Bot protection |
| GCP Cloud Storage | DB seeding, api | Database backups and template logos |
| GitHub / GitLab / Bitbucket | deploy-web, api | Repository import for CI-based deploys |
| Sentry | most apps | Error tracking |
| OpenTelemetry | all Node apps (via packages/instrumentation) |
Traces and metrics |
| Fluentd | api | Log forwarding (Pino logs) |
| Fluent Bit / Datadog | log-collector | Pod log and K8s event forwarding |
| CoinGecko / Keybase / IP geolocation | indexer | Price history, validator info, provider location |
| Confidential-compute attestation (Intel / AMD / NVIDIA) | api | TEE / confidential-compute attestation |
Apps are deployed to Kubernetes via Helm, with one values file per environment in .helm/. Environments are matrixed across prod / staging by mainnet / sandbox / testnet.
%%{init: {'theme':'base','themeVariables':{'fontFamily':'Geist, Inter, system-ui, sans-serif','fontSize':'14px','background':'#171717','primaryColor':'#242422','primaryTextColor':'#e7e3da','primaryBorderColor':'#45423c','lineColor':'#8f8f8f','textColor':'#d9d5cd','clusterBkg':'#1a1a1a','clusterBorder':'#3a3a3a','titleColor':'#f5f2ec','edgeLabelBackground':'#1a1a1a'},'flowchart':{'curve':'basis','nodeSpacing':45,'rankSpacing':60}}}%%
flowchart TB
classDef primary fill:#3a1a1d,stroke:#ff5a63,color:#ffb3b7;
classDef svc fill:#262421,stroke:#47433c,color:#d9d3c8;
classDef env fill:#201e1c,stroke:#45423c,color:#cfc9be;
subgraph k8s["Kubernetes (Helm releases)"]
A["console-api<br/>3 replicas + CronJobs"]:::primary
W["console-web · deploy-web"]:::svc
S["stats-web"]:::svc
I["indexer"]:::svc
PIv["provider-inventory"]:::svc
end
subgraph cron["api CronJobs"]
C1["top-up-deployments · hourly"]:::env
C2["refill-wallets · :45 hourly"]:::env
C3["cleanup-stale-deployments · 10:30 and 22:30"]:::env
C4["gpu-pricing-bot · every 6 h"]:::env
C5["mint-act · 06:00"]:::env
end
A --- cron
ENVS["Environments · prod / staging × mainnet / sandbox / testnet"]:::env
k8s --- ENVS
subgraph providers["Akash provider clusters"]
LC["log-collector"]:::svc
end
The api release also runs scheduled CronJobs (defined inline in its values file), each invoking dist/console.js <command>. Not every service has its own Helm values file in this repo: tx-signer, notifications, provider-proxy, and provider-console are deployed through other pipelines, and log-collector runs on provider clusters. provider-console's primary backend is a separate Go repository.