Deployment and hosting
Atamaia in production is a .NET 10 API, a React SPA, an Astro landing page, and PostgreSQL with pgvector. This page is the running topology.
Canonical API: https://api.atamaia.ai. Local setup: Getting started. Layer map: Architecture.
What is live
Internet
|
Cloudflare
|
atamaia.ai landing site
app.atamaia.ai SPA
api.atamaia.ai API only
mcp.atamaia.ai API only
docs.atamaia.ai redirect to https://atamaia.ai/docs
support.atamaia.ai redirect to https://atamaia.ai/support
|
Caddy on the public VM
|
systemd unit atamaia on 127.0.0.1:5000
|
PostgreSQL + pgvectorapp.atamaia.ai serves the SPA. Clients should use api.atamaia.ai for REST and MCP. Unauthenticated GET /mcp on the API host returns 401. X-Correlation-Id is set on /health. See Logging.
How a release ships
deploy-prod.sh runs on the dev box. It does not build on prod.
- Optional tests.
- Publish
Atamaia.Serverinto a staging directory, not the live folder. - Generate an idempotent EF SQL script.
- Build the landing site and SPA unless skipped.
- Optionally rebuild the code graph and sync those tables. Graph rows are a derived index, the only data allowed to flow dev to prod.
- Dump prod before applying the script.
- Apply
migrate.sql. - Rsync binaries to
/opt/atamaia/publish. - Restart the
atamaiaunit. - Smoke-test
/health.
Schema flows dev to prod. Application data never does. Prod is canonical for identity, memory, facts, chat, and agent runs. The script will not restore dev data onto prod.
Prod has the .NET runtime only. No SDK, no source tree, no dotnet-ef. Migrations travel as SQL. The pre-migration dump is left in place. The script prints the restore command and does not auto-restore.
Edge
TLS is DNS-01 via Cloudflare. The deployed config is deploy/Caddyfile.prod.
index.html is Cache-Control: no-cache, so a cached index cannot point at a missing bundle. /assets/* 404s instead of falling through to index.html. Brand aliases redirect to https://atamaia.ai.
Configuration
Secrets live in /etc/atamaia/atamaia.env, mode 600. They do not live in committed JSON. A double underscore maps to a colon: Encryption__Key is Encryption:Key.
| Variable | Role |
|---|---|
ConnectionStrings__Atamaia |
PostgreSQL |
Encryption__Key |
AES-256 root key, base64, 32 bytes |
Jwt__Key |
HMAC signing key |
Jwt__Issuer, Jwt__Audience |
Token validation |
CF_API_TOKEN |
Caddy DNS-01 |
Do not use ConnectionStrings__DefaultConnection, Jwt__SecretKey, or Encryption__MasterKey. A known-compromised encryption key is refused at startup. Do not put a live key in appsettings.json.
Kestrel listens on port 5000. Caddy binds 443. There is no Redis connection string in the running app. Sessions are not stored in Redis.
Backups
| Unit | Schedule | Notes |
|---|---|---|
atamaia-backup.timer |
daily 02:30 | age-encrypted dumps, 30-day retention, at least 7 kept |
atamaia-backup-verify.timer |
Sunday 03:30 | decrypts and restores; the writer cannot decrypt |
[email protected] |
on failure | writes a Critical log in the Data domain |
A successful backup and a restorable backup are different facts. A Critical row is not an alert until a log subscription matches that domain and level. Hydration systemHealth is empty when nothing is subscribed. See Logging.
Dumps go to the encrypted data volume. Do not write a plaintext dump to the root disk.
Web search uses a SearXNG sidecar (deploy/searxng/). That is not the API. See Web search.
Checklist
- Cloudflare in front. Origin not advertised.
Encryption__KeyandJwt__Keyset, not in git.ConnectionStrings__Atamaiaonly in/etc/atamaia/atamaia.env.- PostgreSQL not reachable from the public internet.
- CORS is the committed origin list, not
*. - Backup verify has run. Failure alerts are wired.
- A log subscription exists for every domain the alerter writes.
Last updated