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¶
stagingbranch = staging VPS (auto-deployed via self-hosted runner)mainbranch = 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_ROOTconstant readsNEXT_PUBLIC_ASTRADIAL_ENV=stagingat build time - LogsUpdate:
X-Astradial-Env: stagingheader routes writes toastrapbx_stage/ - Firestore rules: parallel match blocks for both
astrapbx/*andastrapbx_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_environmentflag controls dispatch; per-orgis_defaultflag drives outbound caller ID; per-useroutbound_didcolumn available for user-specific CID - Admin UI at
/admin/didslets admins toggle a DID betweenprod/staging/osswithout SQL - Every push to
maintriggers async-stagingworkflow that fast-forwards staging to main, so deleting staging via--delete-branchauto-recreates it
See DID Routing Environments and Outbound Caller ID for the end-to-end routing model.
Key differences from prod¶
ASTRADIAL_ENV=stagingin astrapbx.envNEXT_PUBLIC_ASTRADIAL_ENV=stagingin editor.env.localCALL_LOG_WEBHOOK_URLempty (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)