Data regions

An org lives in one region. Its Postgres, its worker, its uploads and its audit log never leave it. Today Harkly Cloud runs one region, us (Railway, US West). The eu region is designed and wired but not provisioned; this page is what turning it on takes.

How routing works

The public gateway (api.harkly.app) is the only shared piece. It decides per request which region's core to call:

  1. x-harkly-region: eu on the request, when the caller already knows (the dashboard sends it when creating an org in a chosen region).
  2. Otherwise the org on the request — x-harkly-public-key for the SDK, widgets and portal; x-harkly-org or ?org= for admin calls — is looked up: the gateway asks every region's core GET /regions/lookup?publicKey=… (or ?org=<slug or id>) in parallel and pins the caller to whoever answers 200. The answer is cached in Redis for an hour; a miss for a minute.
  3. Anything else (sign-in, listing your orgs, creating an org without a region) goes to the home region.

Every proxied response carries x-harkly-region so you can see where it landed. With one region configured, none of this runs — the gateway forwards to CORE_BASE_URL exactly as before.

Core refuses to create an org for a region it doesn't serve (POST /orgs with dataRegion: "eu" on the us core is a 400); the gateway is expected to have routed it. Orgs are never moved between regions.

Configuration

servicevariablemeaning
gatewayHARKLY_REGIONthe home region, default us
gatewayREGION_CORE_URLSeu=https://core-eu.internal,… — other regions' private core URLs
coreHARKLY_REGIONwhich region this deployment is
coreHARKLY_REGIONSevery region orgs may choose, e.g. us,eu (GET /regions reports it)

Self-hosted installs are one region by definition; leave all four unset.

Provisioning eu

  1. A second Railway project (or environment) in an EU region: Postgres, Redis, core, worker. Same images, same env, plus HARKLY_REGION=eu.
  2. Set HARKLY_REGIONS=us,eu on both cores and REGION_CORE_URLS on the gateway pointing at the EU core's private URL.
  3. Uploads: the EU core needs its own storage bucket in the EU.
  4. Email, Slack, Linear, GitHub, Jira, HubSpot, Salesforce credentials are the same apps; their callbacks all pass through the gateway, so nothing changes on the providers' side.

What stays per region

  • Admin sessions. Better Auth's tables live in each region's Postgres. An admin who has orgs in both regions signs in twice; the dashboard keeps one cookie per region. This is deliberate: an SSO/session store that spans regions would put EU admins' identities in the US.
  • Audit log, notifications, reports, AI runs. All keyed to the org.
  • Telemetry pings from self-hosted installs land wherever the install points them; they carry no content either way.

What is global

  • The gateway and its Redis (rate-limit counters and the region cache — public keys and slugs, no personal data).
  • The marketing site and this documentation.
  • Stripe: a customer record per org, in Stripe's own regions.