Skip to content

Staging Environment

Full production clone on a separate VPS for testing changes before deploying to prod.

Servers

Prod Staging
VPS 89.116.31.109 94.136.188.221
Editor editor.astradial.com stageeditor.astradial.com
PBX API devpbx.astradial.com stagepbx.astradial.com
Bots gateway.astradial.com stagebots.astradial.com
SIP devsip.astradial.com stagesip.astradial.com:5080

All stage* hostnames are behind Cloudflare Access (team email allowlist).

Architecture (Option A)

Tata PSTN → NUC (10.10.10.2) → Prod Cloud (10.10.10.1)
                                    │ dispatch by DID
                         ┌──────────┴──────────┐
                    other DIDs            918065978001/003
                         │                      │
                    prod tenants          WireGuard tunnel
                                    Staging Cloud (10.10.10.3)

NUC stays dumb — forwards ALL DIDs to prod. Prod dispatches staging DIDs via cloud-endpoint-stage PJSIP endpoint over WireGuard.

Branch model

feature/* → PR → staging → merge → PR → main
  • staging branch = staging VPS (auto-deployed via self-hosted runner)
  • main branch = prod VPS (manual deploy)
  • 4 repos: astrapbx, astradial-editor, workflow-engine, pipecat-flow

Self-hosted runners

4 runners on the staging VPS at /opt/runners/<repo>/, registered per-repo. Push to staging branch triggers .github/workflows/deploy-staging.yml → rsync to /opt/<app> → build → reload.

Firestore isolation

Staging writes to astrapbx_stage/{orgId}/... instead of astrapbx/{orgId}/...:

  • Editor: ASTRAPBX_ROOT constant reads NEXT_PUBLIC_ASTRADIAL_ENV=staging at build time
  • LogsUpdate: X-Astradial-Env: staging header routes writes to astrapbx_stage/
  • Firestore rules: parallel match blocks for both astrapbx/* and astrapbx_stage/*

Tenant scope

Staging has only AstraPrivate org. Other prod orgs (Acme, TechStart, GrandEstancia) are not on staging.

SIP credentials (staging)

Ext Username Password Host
1001 org_mna9x47k_1001 Hari1001Pass stagesip.astradial.com:5080
0986 org_mna9x47k_0986 4e5dfd2f-891 stagesip.astradial.com:5080

Post-cutover (Apr 17) state

After the monorepo cutover on 2026-04-17:

  • Prod and staging both run the same monorepo code (astradial/astradial-platform)
  • Prod's dispatcher forwards staging-flagged DIDs (routing_environment='staging') to staging via the prod→staging WireGuard tunnel (cloud-endpoint-stage)
  • Staging DB was cleaned of ~19 stray prod-cloned DIDs + the Dharan org (staging is now astraprivate-only per the original design)
  • Per-DID routing_environment flag controls dispatch; per-org is_default flag drives outbound caller ID; per-user outbound_did column available for user-specific CID
  • Admin UI at /admin/dids lets admins toggle a DID between prod / staging / oss without SQL
  • Every push to main triggers a sync-staging workflow that fast-forwards staging to main, so deleting staging via --delete-branch auto-recreates it

See DID Routing Environments and Outbound Caller ID for the end-to-end routing model.

Key differences from prod

  • ASTRADIAL_ENV=staging in astrapbx .env
  • NEXT_PUBLIC_ASTRADIAL_ENV=staging in editor .env.local
  • CALL_LOG_WEBHOOK_URL empty (staging auto-tickets go via CDR poller)
  • GCS_BUCKET=astradial-recordings-stage
  • All DB passwords are staging-specific (in ~/AstradialDevelopment/staging-setup/secrets/staging.env)