Network & DNS
AIQLick runs in eu-north-1 (Stockholm). The post-Phase-7 layout puts
all backend + AI traffic behind a public ALB with ECS Fargate tasks in
private subnets; the only public-IP workloads left are the Jitsi EC2
and a separate support-backend-alb. DNS is managed externally on
one.com (no Route 53).
VPC Configuration
| Property | Value |
|---|---|
| VPC | Default VPC |
| CIDR | 172.31.0.0/16 |
| Region | eu-north-1 (Stockholm) |
| Public subnets | Default /20s across 3 AZs (172.31.0.0/20, 172.31.16.0/20, 172.31.32.0/20) |
| Private subnets | 172.31.96.0/20 (AZ-A), 172.31.112.0/20 (AZ-B), 172.31.128.0/20 (AZ-C) |
| Internet Gateway | Default (attached to VPC) |
| NAT Gateway | One in AZ-A (aiqlick-nat-eu-north-1a) — egress for cross-region eu-west-1 calls (Rekognition / Transcribe / Polly) |
| VPC endpoints | 9 Interface (ecr.api, ecr.dkr, logs, secretsmanager, sqs, sns, events, appconfig, appconfigdata, bedrock-runtime) + 1 S3 Gateway |
Private subnets host every ECS Fargate task (backend + BG dev/prod). Public subnets host the public ALB, the NAT gateway, and the Jitsi EC2. The four legacy backend/AI EC2s that previously also lived in the public subnets were terminated in the Phase 7 cleanup of 2026-05-14.
Load balancers
| ALB | Scheme | Listener | Cert | Purpose |
|---|---|---|---|---|
aiqlick-public-alb | internet-facing | HTTPS:443 + HTTP:80 (plain forward) | ACM api.aiqlick.com SAN api-dev.aiqlick.com (eu-north-1, 644abb95-…) | Public backend traffic. Host-header rules: api.aiqlick.com → tg-backend-prod, api-dev.aiqlick.com → tg-backend-dev. |
aiqlick-internal-alb | internal | HTTP:80 | None (private VPC, internal-service JWT auth) | Backend → BG. Host-header rules: bg-prod.aiqlick.local → tg-bg-prod, bg-dev.aiqlick.local → tg-bg-dev. |
support-backend-alb | internet-facing | (per support-backend-rust setup) | (per support-backend-rust setup) | Standalone Rust support-backend service. Documented in support-backend-rust/CLAUDE.md. |
Sticky sessions are enabled on the public ALB (lb_cookie, 1h) for
WebSocket subscriptions and SSE — without them a client reconnect
under load lands on a different task and loses the in-process
subscription state.
Security Groups
The post-Phase-7 SG layout enforces direction-of-flow:
public-alb-sg :443 ← 0.0.0.0/0
▼
ecs-backend-sg :4001 ← public-alb-sg
▼
internal-alb-sg :80 ← ecs-backend-sg
▼
ecs-bg-sg :8000 ← internal-alb-sg
| SG | Ingress | Notes |
|---|---|---|
aiqlick-public-alb-sg | :443 ← 0.0.0.0/0, :80 ← 0.0.0.0/0 | Public ALB |
aiqlick-ecs-backend-sg | :4001 ← aiqlick-public-alb-sg | ECS backend tasks |
aiqlick-internal-alb-sg | :80 ← aiqlick-ecs-backend-sg | Internal ALB |
aiqlick-ecs-bg-sg | :8000 ← aiqlick-internal-alb-sg, :8000 ← aiqlick-ecs-backend-sg (direct, fallback for Cloud Map name resolution to task ENI) | ECS bg-tasks |
aiqlick-vpc-endpoint-sg | :443 ← 172.31.0.0/16 | Interface endpoints |
jitsi-sg | TCP :22 / :80 / :443, UDP :10000 (JVB media), UDP :20000-20050 (Jigasi RTP) | Jitsi EC2 |
aiqlick-elasticache-sg | :6379 ← 172.31.0.0/16 | Reserved — primary Redis is Redis Cloud (external), this SG is unused today. |
The legacy aiqlick-backend-sg and aiqlick-ai-dev-private-sg were
deleted in the Phase 7 cleanup of 2026-05-14
along with the four EC2 hosts they protected.
DNS Configuration
All aiqlick.com records live in one.com. There is no Route 53
public zone. Records point at the public ALB DNS, Amplify endpoints,
CloudFront distributions, or (for legacy reasons) directly at EC2
public IPs.
| Domain | Type | Target | Service |
|---|---|---|---|
api.aiqlick.com | CNAME | aiqlick-public-alb-2032682590.eu-north-1.elb.amazonaws.com | Backend prod (ECS Fargate via host-header rule) |
api-dev.aiqlick.com | CNAME | aiqlick-public-alb-2032682590.eu-north-1.elb.amazonaws.com | Backend dev (ECS Fargate via host-header rule) |
ai.aiqlick.com | (deleted 2026-05-14) | one.com parking 46.30.213.40 | Record removed from one.com after the Phase 7 cleanup. The bare hostname now falls through to one.com's parking response (Apache 500) — normal one.com behavior for any unconfigured subdomain on a one.com-hosted zone. |
ai-dev.aiqlick.com | (deleted 2026-05-14) | one.com parking 46.30.213.40 | Same disposition as ai.aiqlick.com. |
book.aiqlick.com | A | 16.16.21.64 | Jitsi Meet (production EC2) |
www.aiqlick.com | CNAME | Amplify d36z3aolzpz5md.cloudfront.net | Frontend prod |
dev.aiqlick.com | CNAME | Amplify distribution | Frontend dev |
storage.aiqlick.com | CNAME | CloudFront E4Z4UJZ2HEV46 | File CDN prod |
storage-dev.aiqlick.com | CNAME | CloudFront E1PLDND9V3L62Y | File CDN dev |
docs.aiqlick.com | CNAME | CloudFront E158FQTQZ7MU1B | Documentation prod |
docs-dev.aiqlick.com | CNAME | CloudFront E1ZZP7EF6S8BA | Documentation dev |
bg-{dev,prod}.aiqlick.local | A (private) | Cloud Map → ECS task ENIs | Internal-only — backend → BG via internal ALB |
There is no AWS Route 53 zone for aiqlick.com. DNS changes must be
done in the one.com control panel — they cannot be automated through
the AWS CLI or Terraform. This includes the ACM validation CNAMEs
(_HASH.api(.dev).aiqlick.com. → *.acm-validations.aws.) which ACM
re-checks at every renewal. Don't delete those after issuance or the
next renewal silently breaks.
SSL/TLS
| Service | Cert source | Cert region | Domains |
|---|---|---|---|
aiqlick-public-alb | ACM | eu-north-1 | api.aiqlick.com (CN), api-dev.aiqlick.com (SAN) — 644abb95-5a71-4d53-8118-e42630ad8096 |
| CloudFront (storage) | ACM | us-east-1 | storage.aiqlick.com, storage-dev.aiqlick.com |
| CloudFront (docs) | ACM | us-east-1 | docs.aiqlick.com, docs-dev.aiqlick.com |
| Amplify | ACM | us-east-1 | www.aiqlick.com, dev.aiqlick.com |
| Jitsi EC2 | Let's Encrypt (Nginx + certbot) | n/a | book.aiqlick.com |
| Legacy backend/AI EC2s (zombies) | Let's Encrypt (Caddy) | n/a | Reachable but unused — pending termination |
- CloudFront / Amplify require certificates in us-east-1, regardless of where the origin lives.
- ALBs require certificates in the same region as the ALB.
Traffic flows
API request
Backend → background-tasks (internal)
The backend addresses bg-tasks via the Cloud Map name
bg-{env}.aiqlick.local. This name is registered to the internal
ALB (not to the task ENIs directly), so requests get host-header
routed to the right target group. This is what AI_BACKEND_GRAPHQL_URL
in the backend task definition resolves to.
WebSocket subscriptions
| URL | Purpose |
|---|---|
wss://api.aiqlick.com/graphql | Backend GraphQL subscriptions (notifications, log streaming, support events). Also subscribeAi — the gateway's single-document proxy for every AI subscription (CV extraction, job parsing, agent chat, etc.). See AI Gateway. |
wss://api.aiqlick.com/voice/ws/{conversationId} | Voice chat (raw PCM proxy through the gateway to background-tasks). |
wss://ai.aiqlick.com/... URLs no longer exist in the frontend code —
the gateway swallowed all four AI WS endpoints. The ai.aiqlick.com
DNS record itself still resolves (to a zombie EC2) but nothing the
frontend ships actually opens a connection to it.
WebRTC media (Jitsi)
| Source | Protocol | Destination | Purpose |
|---|---|---|---|
| Browser | HTTPS:443 | book.aiqlick.com | Web UI + signaling |
| Browser | UDP:10000 | book.aiqlick.com | JVB media (video/audio) |
| Jigasi | UDP:20000+ | book.aiqlick.com | Transcription audio (RTP) |
Cross-region calls
ECS bg-tasks in eu-north-1 makes cross-region calls to AWS services in eu-west-1 (Rekognition for face detection, Transcribe for STT, Polly for TTS). These travel over the AWS backbone via the NAT gateway — ~20-30ms additional latency, acceptable since face detection is one-shot and transcription latency is hidden by streaming.
Environment isolation
Per-env isolation lives at the IAM, security-group, and SQS-queue level — not at the ECS-cluster or VPC level (one cluster + one VPC hosts both envs).
| Boundary | Dev | Prod |
|---|---|---|
| ECS service | backend-dev, bg-tasks-dev | backend-prod, bg-tasks-prod |
| Public ALB hostname | api-dev.aiqlick.com | api.aiqlick.com |
| Cloud Map service | bg-dev.aiqlick.local | bg-prod.aiqlick.local |
| RDS | aiqlick-postgres-dev | aiqlick-postgres |
| S3 uploads bucket | aiqlick-dev-uploads | aiqlick-uploads |
| Frontend (Amplify branch) | dev (dev.aiqlick.com) | main (www.aiqlick.com) |
| Secrets | aiqlick-backend/development | aiqlick-backend/production |
| Task roles | aiqlick-ecs-{backend,bg}-task-role-dev | aiqlick-ecs-{backend,bg}-task-role-prod |
| SQS queues | aiqlick-*-dev | aiqlick-*-prod |
Troubleshooting
*.aiqlick.com returns a 500 from a non-AWS IP
If a hostname like https://api.aiqlick.com returns a generic
Apache/Varnish 500 ("contact the server administrator at
support@one.com") and resolves to 46.30.213.40 (or anything in the
46.30.213.0/24 block — see whois),
the DNS record is parked at one.com rather than pointing at the
intended AWS resource.
This typically happens when a record is deleted in the one.com control panel — one.com falls back to serving its own infrastructure for the bare hostname.
curl -sS -o /dev/null -w "Status: %{http_code} IP: %{remote_ip}\n" https://api.aiqlick.com/health
dig +short @ns01.one.com api.aiqlick.com
If the IP is owned by One.com A/S (Denmark, AS31034), re-create the
record in the one.com control panel. The expected target for the
public ALB hostnames is:
api.aiqlick.com ALIAS/CNAME aiqlick-public-alb-2032682590.eu-north-1.elb.amazonaws.com.
api-dev.aiqlick.com ALIAS/CNAME aiqlick-public-alb-2032682590.eu-north-1.elb.amazonaws.com.
Before flipping DNS back, confirm the ALB has a :443 listener with a
valid ACM cert covering the hostname (see "SSL/TLS" above).
ai-dev.aiqlick.com / ai.aiqlick.com health check fails
Expected — those records were deleted from one.com on 2026-05-14 as
part of the Phase 7 cleanup.
Both hostnames now fall through to one.com's parking IP 46.30.213.40
which serves an Apache 500 (see "*.aiqlick.com returns a 500 from a
non-AWS IP" above). The FE doesn't call them — it goes through
api(.dev).aiqlick.com via the
AI gateway. If you want
to reach dev background-tasks, exec into the bg-tasks-dev ECS task
or call the internal Cloud Map name from inside the VPC — see
SSM Operations.