Operations and configuration
This page is the cross-service operating guide. Component pages explain feature behavior; this page explains how the deployed parts discover one another, which dependencies are hard requirements, where state lives, and how to investigate failures.
Deployment shape
All four application repositories provide container definitions. In the hosted layout, Traefik terminates TLS and routes host names to the frontend, Trader-BE, News-Analyzer, and LLM-Wrapper. Trader-BE may also sit behind its own Nginx response cache. Containers join the shared deployment network and address one another by service name in development or by HTTPS host in production.
Port numbers describe the process inside its container. A host, compose, or Traefik port can differ. In particular, News-Analyzer listens on 8080 in its current application configuration while older documentation and some deployment naming refer to 8087. Service discovery should use the actual container listener and router configuration, not an old diagram.
Dependency matrix
| Component | Hard dependency for startup | Optional or degradable dependency |
|---|---|---|
| Trader-FE browser | Static assets and reachable BFF for API work | Individual page APIs can fail independently |
| Trader-FE BFF | Listener and configured Trader-BE upstream | No database or cache of its own |
| Trader-BE | Redis | PostgreSQL can fall back in memory; provider and PayOS keys affect their features |
| News-Analyzer | Redis for normal context and stage operation, reachable LLM-Wrapper for model stages | PostgreSQL and individual data-provider keys can degrade coverage |
| LLM-Wrapper | Readable model file and at least one Gemini key | Additional keys improve rate-limit capacity |
Production should not intentionally use Trader-BE’s database-free mode. It loses balances, settings, packs, purchases, and history on restart and cannot provide reliable payment behavior.
Service discovery
The browser uses same-origin /api addresses. The production frontend server forwards those requests to the Rust BFF. The BFF forwards to Trader-BE using its configured upstream address.
Trader-BE uses SENTINEL_URL for authentication. For News-Analyzer it uses the internal service address in development and constructs an HTTPS host from DOMAIN_NAME outside development. News-Analyzer uses LLM_WRAPPER_URL. PostgreSQL and Redis use their connection URLs.
Check all of these from inside the calling container. A host-reachable URL can still be unreachable on the container network, and localhost inside a container refers to that container itself.
Secret and configuration ownership
| Secret or setting | Consumed by | Purpose |
|---|---|---|
| FINAI_JWT_SECRET | Trader-BE and the token issuer | Local verification of protected requests |
| NEWS_API_KEY | News-Analyzer | NewsAPI article coverage |
| FINNHUB_API_KEY | News-Analyzer and optionally Trader-BE | News, fundamentals, and enrichment |
| Gemini key file | LLM-Wrapper | Google model access and rotation |
| PAYOS_CLIENT_ID, PAYOS_API_KEY, PAYOS_CHECKSUM_KEY | Trader-BE | Checkout, status, signatures, and webhook handling |
| PostgreSQL URLs | Trader-BE and News-Analyzer | Durable application and analyzer data |
| REDIS_URL | Trader-BE and News-Analyzer | Shared caches and temporary contexts |
Secrets must be injected at deployment and excluded from images, repositories, logs, browser bundles, and documentation. The Gemini key file is mounted into LLM-Wrapper. Browser runtime configuration must contain only public values.
Database migrations
Trader-BE runs its SQL migrations when a PostgreSQL pool is established. The migrations create and evolve users and credits, analysis history, purchases, payment transactions, credit packs, roles, recent searches, popularity, settings, runtime cache settings, analysis summaries, overview payloads, and the link to News-Analyzer runs.
News-Analyzer migrations create historical market tables, ingestion-job coordination, analysis runs, and stage outputs. Migration permissions are therefore required for the service account during startup unless the deployment applies migrations separately.
Before rollback, verify whether the previous application version can read the newer schema. Database backups are more important than Redis backups because caches can be reconstructed while payment and history rows cannot.
Redis ownership and invalidation
Both Rust services can share a Redis deployment because their key prefixes distinguish domains. Market profiles intentionally use the shared profile: concept so a profile collected by News-Analyzer can serve Trader-BE enrichment.
Do not flush production Redis casually. A flush removes more than price acceleration: it also removes one-hour interactive analysis contexts and may force expensive fact, claim, and stage regeneration. It does not remove durable analysis runs or Trader-BE history.
When prompts, schemas, or calculation semantics change, increment the relevant cache version or version field. Time-to-live expiration alone can leave old semantic output visible for its entire lifetime.
Health and readiness
Process health endpoints show that the HTTP server is accepting requests. They do not prove every dependency works. A useful readiness suite checks Trader-BE health, a public price or asset request, Redis connectivity, PostgreSQL mode, News-Analyzer health, LLM-Wrapper model listing, and a small controlled structured model call.
Payment readiness additionally requires PayOS credentials, a public HTTPS webhook, correct signature handling, and a test transaction in the gateway environment. Historical-analysis readiness requires analyzer database connectivity and populated price history.
Logging and correlation
Trader-BE and News-Analyzer use structured tracing and include request middleware. LLM-Wrapper uses Go request and retry logs. The edge proxy and BFF should preserve or create a request identifier and forward it downstream.
For one failed analysis, collect the browser time and symbol, BFF status, Trader-BE request identifier and credit transaction, News-Analyzer context or run identifier, LLM model and retry outcome, and the final database state. This distinguishes a browser timeout from an upstream failure and a failed analysis from an analysis that completed after the browser disconnected.
Never log bearer tokens, refresh tokens, Gemini keys, PayOS checksum secrets, or full payment payloads containing sensitive details. Prompt logging should be disabled or redacted in production because collected articles and user-selected data flow through prompts.
Common investigations
The UI receives 401 repeatedly
Verify the access token is attached, the frontend has exactly one refresh in progress, the refresh contract uses the expected field names, Sentinel is reachable through Trader-BE, and FINAI_JWT_SECRET matches the issuer. A stale role in local storage affects display, but Trader-BE’s token claim decides authorization.
A user was charged but saw no result
Locate the negative credit transaction and its request identifier. Check whether News-Analyzer completed and persisted a run after the frontend’s five-minute timeout. Check Trader-BE history and analysis cache. The current system refunds only completed results below 0.25 confidence, not every downstream failure, so manual reconciliation may be required.
Supporting tabs fail after the overview works
Confirm the frontend retained the initialization context identifier rather than sending the analysis-run or history UUID. Check whether more than one hour elapsed or Redis was cleared. Start a new initialization when the context is gone.
Results look stale
Inspect browser page-local tab memory, Nginx response cache, Trader-BE Redis, News-Analyzer profile and stage caches, and runtime cache settings. Each layer has a different lifetime. Confirm the cache key includes the requested symbol, lookback, interval, range, and prompt or schema version where relevant.
News or fundamentals are thin
Check provider credentials and rate limits, symbol resolution, asset type, source filtering, extraction warnings, and provider-specific availability. Thin evidence should lower confidence and populate unavailable fields rather than be hidden.
Model calls fail intermittently
Inspect the requested model, local model configuration, key rotation logs, 429 exclusions, temporary Google server failures, and all-key exhaustion. A configured model can be unavailable to the Google project even though it appears in /v1/models.
Release verification
For every coordinated release, verify anonymous search, asset detail, prices, chart history, and news preview; registration, login, refresh, Google login where configured, and logout; analysis initialization and one paid overview; all five supporting tabs; low-balance 402 behavior; low-confidence refund behavior in a controlled test; history ownership and deletion; settings persistence; active pack listing and a gateway test payment; admin denial and admin workflows; English and Vietnamese rendering; service restart with persistent state; and a multi-replica preview refresh without duplicate work.
API-contract changes should be released from downstream to upstream when compatibility requires it: make the receiving service accept both forms, update the caller, then remove the old form in a later release.