Docs Técnicas
atlas-ingress
atlas-ingress is a small public REST ingress/proxy that sits in front of atlas-node's REST API. Its job is to expose a tightly allowlisted public surface for reads, transaction submission, and SSE events without exposing the full node API directly.
Summary
atlas-ingress is a small public REST ingress/proxy that sits in front of atlas-node's REST API. Its job is to expose a tightly allowlisted public surface for reads, transaction submission, and SSE events without exposing the full node API directly.
This crate does not embed the node runtime and does not depend on atlas-node as a Rust library. It talks to an upstream node over HTTP(S), which makes it a decoupled edge service rather than an internal runtime module.
Why This Crate Exists
- expose a controlled public REST surface in front of
atlas-node - block admin-only and mutation-heavy routes that should not be internet-facing
- centralize CORS policy and request-body limits for the public edge
- forward client IP context to the upstream node
- keep the public ingress deployable as a separate lightweight process
Current Role In The Workspace
atlas-ingress currently owns four focused concerns:
- request filtering by path and method before any upstream call
- reverse-proxy forwarding to the configured
atlas-nodeREST upstream - CORS and body-size policy for the public edge
- a minimal standalone binary that can be started in dev, demo, or containerized environments
This is one of the clearest single-purpose crates in the workspace.
Public Surface
Top-Level Surface
The crate is effectively a single-module library plus a binary:
- `lib.rs`
- `main.rs`
Main Public API
IngressConfigAppStateIngressErrorbuild_router(...)normalize_origins(...)build_http_client(...)default_state(...)
Package Targets
- library target
atlas_ingress - binary target
atlas-ingress
Key Modules
- `lib.rs`: router, request filtering, upstream URL construction, CORS setup, upstream validation, and tests
- `main.rs`: CLI/env parsing, default config assembly, state creation, and Axum server startup
Inputs And Outputs
Inputs
- bind address and port
- upstream
atlas-nodeREST base URL - body limit and request timeout values
- configured CORS origins
- incoming public HTTP requests
Outputs
- a public Axum HTTP server
- proxied requests to the upstream
atlas-node - forwarded client IP headers:
x-forwarded-forandx-real-ip - streamed upstream responses back to clients
- structured error responses when a route, method, payload, or upstream is rejected
Internal Dependencies
Workspace Dependencies
- none
That is a notable boundary: atlas-ingress is intentionally independent from the rest of the Rust workspace at the crate-dependency level.
External Dependencies That Shape The Design
axumfor the public HTTP server and routingreqwestfor proxying requests to the upstream nodetower-httpfor CORS handlingclapfor CLI/env configurationtokiofor async runtimetracingandtracing-subscriberfor operational logging
Used By
No Rust crate currently depends directly on atlas-ingress.
Operationally, the package is used as a standalone service in local and demo flows:
- `start_ingress.sh`: convenience launcher for local ingress startup
- `start_demo_dev.sh`: builds and runs the ingress alongside the demo stack
- `start_demo_prod.sh`: production-style demo launcher also builds the ingress
- `docker/supervisord.conf`: supervisord runs
atlas-ingressas its own process in the container stack - `docker/Dockerfile`: builds and copies the standalone ingress binary
Request Model
Health Surface
The crate exposes a small local health endpoint:
GET /healthz
It reports:
- static
"ok"status - the configured upstream node URL
Allowlisted Public Surface
All other requests go through the fallback proxy path in `proxy_request(...)`.
The current public surface is explicitly allowlisted in code, not inferred from upstream behavior.
Allowed reads include:
/api/transactions/api/events/api/mempool/api/balance/api/global-balance/api/accounts/api/tokens/api/audit/proposals/api/institutions/api/aec/*/api/audit/proposal/*/api/aliases/*/api/kyc/*
Allowed writes are much narrower:
POST /api/transaction
Anything outside that surface, such as admin-style paths like /api/accounts/open or /api/kyc/issue, is rejected before proxying.
Method Policy
Method filtering is also explicit:
GETonly for allowlisted public read pathsPOSTonly for/api/transactionOPTIONSonly for known public paths
This means ingress policy is enforced locally even if the upstream node would accept more.
Proxy Behavior
For accepted requests, the crate:
- builds the upstream URL by concatenating the configured base URL with the incoming path and query
- reads the request body with a configured max size
- forwards selected request headers
- injects client IP information through
x-forwarded-forandx-real-ip - issues the upstream request through a shared
reqwest::Client - streams the upstream body back to the caller
The proxy currently preserves a small response-header subset:
content-typecache-controlx-request-idretry-after
That is enough for JSON APIs and SSE-style responses without trying to mirror every upstream header.
Security And Edge Policy
atlas-ingress is simple, but it still enforces a few meaningful edge constraints.
Upstream URL Validation
default_state(...) validates the configured upstream before the server starts:
- upstream URL must be absolute
- scheme must be
httporhttps - host must be present
- plain
httpis only allowed for loopback upstreams
So a remote upstream must use HTTPS, while local development can still target http://127.0.0.1:3001.
CORS Behavior
If no explicit origins are provided, normalize_origins(...) falls back to a dev-focused localhost allowlist rather than "*".
That default list includes local ports like:
localhost:5173-5177127.0.0.1:5173-5177
If "*" is configured explicitly, the ingress will allow any origin.
Body Limits And Timeouts
The main edge limits are configured through:
body_limit_bytesrequest_timeout_ms
Request bodies that exceed the limit are rejected at the ingress before the upstream sees them.
Runtime And Deployment Model
The binary in `main.rs` is intentionally small:
- initialize tracing
- parse CLI args or env vars
- normalize CORS origins
- validate upstream config and build the shared HTTP client
- build the Axum router
- bind a TCP listener
- serve requests with
ConnectInfo<SocketAddr>enabled
The main runtime env vars are:
ATLAS_INGRESS_BIND_ADDRATLAS_INGRESS_PORTATLAS_INGRESS_NODE_URLATLAS_INGRESS_BODY_LIMIT_BYTESATLAS_INGRESS_REQUEST_TIMEOUT_MSATLAS_INGRESS_CORS_ALLOWED_ORIGINS
Testing
The crate keeps its tests in `lib.rs`.
Coverage includes:
- allowlisted route and method policy
- upstream URL construction with preserved query strings
- default CORS-origin normalization
- upstream validation for loopback HTTP vs remote HTTPS
- router rejection of blocked admin paths
- router rejection of wrong methods before proxying
healthzbehavior- CORS preflight handling
For a small edge service, that is a solid amount of behavioral coverage.
Risks Or Design Tension
Surface Allowlist Must Track `atlas-node`
The ingress hardcodes what public paths exist. That is good for safety, but it creates a maintenance requirement: every time atlas-node changes its intended public REST surface, this crate may need an explicit update.
Header Forwarding Is Intentionally Narrow
Only a selected set of request and response headers is forwarded. That keeps the proxy easier to reason about, but it may surprise future features that assume transparent passthrough.
REST-Only Boundary
This crate only fronts the REST surface. It does not proxy the node's public gRPC or secure proposal gRPC endpoints, so the public ingress story is intentionally partial rather than universal.
Policy Lives In Code, Not Shared Contracts
The list of public paths and methods is encoded directly in lib.rs, not generated from a shared API contract. That keeps it obvious, but also makes drift more likely over time.