Publication update: project provisioned, services created and web online. Destinations, domain and effective configuration are in the checklist and ADR-0014. The historical text below does not describe the current state and should not guide variable configurations.
Implementation update: see the ROADMAP, ADR-0011, ADR-0012 and ADR-0013 for the effective state. The text below preserves the original requirements/designs, not a statement that all infrastructure or external validation details have already occurred.
Status: destination confirmed; topology and configurations below proposed. No Railway service was created at this stage.
Initial topology#
| Service | Process | Exposure |
|---|---|---|
| web | Current Vukem binary, /app/app | Public HTTPS; authenticated pages and APIs |
| engine | Future binary /app/engine | No public domain; connection to broker/feed |
| PostgreSQL | Persistent service | Private network between services |
| backtest | Separate job/process if needed | No resource contention with execution |
Keep the engine continuously active and disable hibernation/serverless on it. Initially one replica; locks and fencing remain necessary during rolling deploy. Do not scale the executor by simply changing replica counts. Additional cache/broker components are added only via ADR. Confirm region, plan, CPU/RAM, budget and backup before deploy, without assuming TimescaleDB is available.
Build and commands#
The current Dockerfile only compiles cmd/app; creating cmd/engine requires changing the build and
image. Do not configure a non-existent start command. The web listens on
0.0.0.0:$PORT; Vite assets are embedded. Keep an unprivileged user.
Proposed pre-deploy: /app/app migrate. Migrations must be compatible with
the old version during overlap and use serialization/lock. Expand the schema,
publish consumers and only then remove fields. Migration failure blocks
promotion. Do not run destructive migrations at the boot of each worker.
On Railway, a Docker start command overrides ENTRYPOINT; provide the full executable. Validate the configuration in staging, without assuming shell expansion. Record one configuration per service when implemented.
Proposed variables#
| Service | Variables | Note |
|---|---|---|
| web | APP_ENV, APP_DEBUG, APP_URL, HOST, PORT | production, debug off, canonical HTTPS URL |
| web | APP_KEY, DATABASE_URL | Railway secrets; persistent key across deploys |
| web | SOCIAL_GOOGLE_CLIENT_ID, SOCIAL_GOOGLE_CLIENT_SECRET | Existing names from the Vukem scaffold |
| web | CACHE_STORE, SESSION_DRIVER | Define shared storage and migrations |
| engine | DATABASE_URL, TRADING_MODE | TRADING_MODE is a proposed new contract |
| engine | BROKER_API_KEY, BROKER_API_SECRET | Proposed generic names; paper only in the recommended scope |
| engine | BROKER_BASE_URL, MARKET_DATA_FEED | Validated against the adapter/mode allowlist |
| engine | ENGINE_ACCOUNT_ID | Proposed new configuration, leadership scope |
| alerts | TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID | Optional, after choosing the channel |
Authorized user IDs and limits will be defined after Q1. Do not send secrets to Inertia props, Vite bundle, logs or documents. The Google callback must match the registered URI, including protocol, domain and path. Login does not require permanent Google access tokens if no Google API will be used.
Health, telemetry and recovery#
/up exists in the starter as liveness; do not expose balances or configurations there.
Propose /ready for the web/dependencies. Railway uses healthcheck for deploy;
continuous monitoring needs its own tool/alerts. The worker reports
heartbeat in the database and internal readiness; it does not depend on public access to the endpoint.
Metrics: age of the last candle, feed delay, processing, broker latency, unknown intents,
divergences, risk reservations, heartbeat, queue/outbox,
login failures and connections. Structured logs with request/event/order IDs;
redact tokens, headers and personal profiles. Retention and targets still pending.
Incident runbook: persist the account block → confirm new entries are blocked → query broker state → cancel according to policy → reconcile → verify positions and exposure → record the cause → resume explicitly. Closing all positions is a separate operation, subject to market/limits; failure or lack of connectivity must appear as pending, never as confirmation of flat.
Restart runbook: acquire leadership → load blocks/versions → reconcile orders and fills → rebuild exposure → validate feed/calendar → enable only if the policy allows. Restore is done in an isolated environment without execution enabled; reconcile against the broker before any resumption. Define RPO/RTO and rehearse the restore; a persistent volume alone is not a backup strategy.
Code rollback does not revert fills or cancel orders. Avoid downgrading the financial schema. The worker must react to SIGTERM by stopping new entries, preserving intents and finalizing a checkpoint; the broker remains the source of confirmation.
The pre-deploy runs in a separate container, with variables and private network, but without mounted volumes. Do not depend on disk writes at this stage. The absence of sticky sessions in the distribution of replicas reinforces shared sessions and states.
Adjustment after round 2#
US: secrets exclusive to Alpaca Paper and an allowlist of the paper host. B3: internal adapter
without external order credentials. Web: OWNER_GOOGLE_EMAIL is a proposed
configuration for the initial gate of the only authorized account. Once bound,
the persisted Google identity prevails; recovery must be audited.