Cloud Platform Architecture
Scope: This page covers the Phony Cloud technical architecture — the control-plane/data-plane split, the stack for each side, security, and API design. For the overall tech stack and OSS components, see Tech Stack. For the platform-level view, see Cloud Platform.
Implementation status
Everything on this page is target design. The Rust engine and CLI exist; the control plane, the agent's cloud protocol, and all hosted services are not built.
System Architecture
Phony Cloud is two systems with a deliberately narrow interface:
- Control plane (hosted by Phony): a thin metadata service — dashboard, orchestration, job state, PII inventory, audit, compliance reports, billing. It never touches customer row data.
- Data plane (customer infrastructure): the
phonyagent, a single static Rust binary that does all data work locally and makes outbound-only connections to the control plane.
┌─────────────────────────────────────────────────────────────────────────┐
│ PHONY CLOUD ARCHITECTURE │
│ (thin hosted control plane + local data plane) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ CONTROL PLANE (hosted) │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Web Dashboard Control API Mock API │ │
│ │ (Nuxt 3 + Vue 3 + TS) (Go) Server (Go) │ │
│ │ • Auth (Auth.js) • /api/v1/* • hosted, │ │
│ │ • Billing (Stripe/ • agent gRPC synthetic │ │
│ │ Paddle) endpoint data only │ │
│ │ • Project / PII UI • job scheduler │ │
│ │ • webhook dispatch │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ PostgreSQL — jobs, schemas, column classifications, │ │ │
│ │ │ snapshot catalog, audit log, report artifacts, billing │ │ │
│ │ │ (METADATA ONLY — never row data, credentials, or models) │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────▲───────────────────────────────────┘ │
│ │ outbound-only gRPC/HTTPS │
│ │ (agent dials out; nothing dials in) │
│ DATA PLANE (customer infrastructure) │
│ ┌───────────────────────────────┴───────────────────────────────────┐ │
│ │ PHONY AGENT (Rust, static binary) │ │
│ │ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────┐ │ │
│ │ │ Schema │ │ Transform │ │ Snapshot │ │ N-gram │ │ │
│ │ │ Analyzer │ │ Pipeline │ │ Engine │ │ Engine + │ │ │
│ │ │ (FK, PII) │ │ (stream) │ │ │ │ Trainer │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ │ ~5M rec/sec │ │ │
│ │ └───────────────┘ │ │
│ │ DB connectors: PostgreSQL, MySQL/MariaDB, SQLite │ │
│ │ Storage: local disk / customer S3-GCS (snapshots, models) │ │
│ │ Also runs standalone via CLI (no cloud account needed) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘The interface between the planes is small by design: the agent pulls job specs and pushes status, schema metadata, column classifications, and report artifacts. That is the entire protocol surface — which is what makes the privacy claim auditable.
Technology Stack
CONTROL PLANE (hosted, thin) DATA PLANE (customer infra)
├── Nuxt 3 dashboard ├── phony agent (Rust, stable)
│ ├── Vue 3 + Composition API │ ├── single static binary
│ ├── TypeScript │ │ (musl / MSVC targets)
│ ├── Tailwind CSS │ ├── embedded N-gram engine
│ ├── Auth.js (authentication) │ ├── DB drivers (Postgres,
│ └── Stripe SDK (billing) │ │ MySQL/MariaDB, SQLite)
├── Go control API │ ├── S3/GCS clients (BYO storage)
│ ├── job/schedule orchestration │ └── outbound gRPC/HTTPS client
│ ├── agent gRPC endpoint ├── licensed per organization
│ └── webhook dispatch │ (offline license file for
├── Go Mock API server │ air-gapped Enterprise)
│ └── embeds Rust engine (CGO) └── distributed via curl installer,
└── PostgreSQL (metadata store) Homebrew, Docker image
DEPLOYMENT
├── Dashboard: Vercel or Docker
├── Control API + Mock API: Docker / Kubernetes (small footprint —
│ no data workloads means no data-plane scaling problem)
├── Agent: runs on customer hosts, CI runners, or a VM next to the DB
└── Enterprise: the entire control plane ships as a self-hostable
Docker Compose / Helm bundle (air-gap capable)Why This Stack?
| Component | Choice | Rationale |
|---|---|---|
| Dashboard | Nuxt | Vue familiar, TypeScript, easy deploy |
| Control API | Go | Fast HTTP/gRPC, simple ops; carries only metadata, so it stays small |
| Agent | Rust | One binary, no runtime deps — critical for "run this inside your prod network"; maximum engine performance (5M rec/sec); memory safe next to production data |
| No Go in the data plane | — | Earlier drafts split sync workers (Go) from the engine (Rust FFI). With the data plane local-first, a single Rust binary is simpler to distribute, audit, and harden |
| Metadata store | PostgreSQL | Boring, reliable; the control plane's state is small |
| Billing | Stripe SDK | After Delaware C-Corp incorporation (see Operations) |
Billing Strategy (Phased)
| Phase | Revenue | Method | Notes |
|---|---|---|---|
| Early | $0-10K MRR | Paddle (MoR) | No company needed, 5% + $0.50 fees |
| Growth | $10K+ MRR | Stripe Billing | After Delaware C-Corp via Stripe Atlas |
Note: See Operations for full incorporation timeline.
Security Model
The security story has two halves that must not be conflated: customer data security (structural — it never leaves their network) and control-plane security (conventional SaaS hardening, applied to metadata).
Customer Data: Never Leaves Your Network
┌─────────────────────────────────────────────────────────────────┐
│ DATA-PLANE SECURITY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Structural guarantees (architecture, not policy): │
│ ├── Row data is read, transformed, and written by the agent │
│ │ inside the customer network — no cloud hop exists │
│ ├── The agent makes OUTBOUND-only connections; no inbound │
│ │ ports, SSH tunnels from the cloud, or VPC peering │
│ ├── DB and storage credentials are agent-local config │
│ │ (env vars / files / OS keychain) — never sent to the cloud │
│ ├── PII-detection samples are analyzed in-process, discarded │
│ └── Trained models stay in customer storage; the cloud sees │
│ only name + schema hash │
│ │
│ Agent hardening: │
│ ├── Static binary, reproducible builds, signed releases │
│ │ (checksums + signature published per release) │
│ ├── Minimal privileges: needs only DB creds you give it and │
│ │ egress to the control plane (one documented endpoint) │
│ ├── Egress allowlist-friendly: single hostname, gRPC/HTTPS 443 │
│ ├── No telemetry beyond the documented metadata protocol │
│ └── Fully operable offline via CLI (cloud is optional) │
│ │
└─────────────────────────────────────────────────────────────────┘Control Plane: Conventional Hardening for Metadata
┌─────────────────────────────────────────────────────────────────┐
│ CONTROL-PLANE SECURITY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ At-rest encryption (AES-256) applies to what the cloud holds: │
│ ├── Schema metadata + column classifications │
│ ├── Report artifacts (PII inventory, KVKK/GDPR packs) │
│ ├── Job history and audit log │
│ └── (There is no row data to encrypt — it is not here) │
│ │
│ API keys & agent identity: │
│ ├── API keys hashed (bcrypt) — original never stored │
│ ├── Prefix visible for identification (pk_xxx...) │
│ ├── Revocable per-key, scoped permissions (read/write/admin) │
│ └── Agents authenticate with per-agent tokens, revocable │
│ from the dashboard (kill switch for a lost host) │
│ │
│ Access control (control plane): │
│ ├── RBAC roles (see Team & Collaboration below) │
│ ├── SSO / SAML (Business+), enforced MFA option │
│ └── Audit log of every control-plane action │
│ │
│ Data residency: │
│ ├── Row data residency is MOOT — rows stay wherever the │
│ │ customer's own infrastructure is │
│ ├── METADATA residency remains: EU orgs → eu.phony.cloud │
│ │ (Frankfurt), US orgs → us.phony.cloud (Virginia) │
│ └── Enterprise: self-hosted control plane = full residency │
│ control, including air-gapped │
│ │
│ Retention (metadata): │
│ ├── Job logs: 90 days │
│ ├── Report artifacts: until deleted by user │
│ └── Audit logs: 1 year (Enterprise: configurable) │
│ │
└─────────────────────────────────────────────────────────────────┘Enterprise: Self-Hosted Control Plane
Enterprise is not a different product — it is the same thin control plane, shipped as a deployable bundle (Docker Compose / Helm) and run inside the customer's network. Air-gapped installs use an offline license file and receive agent/control-plane updates as signed artifacts. Because the control plane holds only metadata, self-hosting it is operationally light.
API Design
Versioning Strategy
┌─────────────────────────────────────────────────────────────────┐
│ API VERSIONING │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Strategy: URL Path Versioning │
│ │
│ Current: /api/v1/... │
│ Future: /api/v2/... │
│ │
│ Versioning Rules: │
│ ├── Major version in URL (/v1, /v2) │
│ ├── Breaking changes require new major version │
│ ├── Additive changes allowed within version │
│ ├── Deprecation notice 6 months before removal │
│ └── Minimum 12 months support for each version │
│ │
│ The agent↔control-plane gRPC protocol is versioned │
│ independently; agents negotiate on connect and old agents │
│ keep working across control-plane deploys. │
│ │
│ Headers: │
│ ├── X-API-Version: Returns current version │
│ ├── X-Deprecation-Date: If endpoint deprecated │
│ └── X-Rate-Limit-*: Rate limiting info │
│ │
└─────────────────────────────────────────────────────────────────┘Webhook System
Webhooks are dispatched by the control plane from job-status metadata (payloads carry counts and durations, never data):
┌─────────────────────────────────────────────────────────────────┐
│ PHONY CLOUD WEBHOOKS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Available Events: │
│ ├── sync.started - Sync job began (on an agent) │
│ ├── sync.completed - Sync job finished successfully │
│ ├── sync.failed - Sync job failed │
│ ├── snapshot.created - Snapshot captured (metadata recorded) │
│ ├── model.trained - Local model training complete │
│ ├── report.ready - PII inventory / compliance pack ready │
│ └── api.deployed - Mock API deployment complete │
│ │
│ Webhook Payload: │
│ { │
│ "event": "sync.completed", │
│ "timestamp": "2026-01-15T10:30:00Z", │
│ "project_id": "proj_abc123", │
│ "data": { │
│ "job_id": "job_xyz789", │
│ "agent_id": "agt_local01", │
│ "rows_processed": 150000, │
│ "duration_seconds": 45 │
│ } │
│ } │
│ │
│ Security: │
│ ├── HMAC-SHA256 signature in X-Phony-Signature header │
│ ├── Timestamp validation (reject >5 min old) │
│ ├── Retry with exponential backoff (3 attempts) │
│ └── Webhook secret per endpoint │
│ │
└─────────────────────────────────────────────────────────────────┘CLI Authentication
The CLI and the agent share credentials; both work fully offline against local databases — cloud auth is only needed for control-plane features.
┌─────────────────────────────────────────────────────────────────┐
│ CLI AUTHENTICATION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Method 1: API Key (Recommended for CI/CD) │
│ $ export PHONY_API_KEY=pk_live_xxxxx │
│ $ phony sync start --project my-project │
│ │
│ Method 2: OAuth Device Flow (Interactive) │
│ $ phony login │
│ → Opens browser: phony.cloud/device │
│ → Enter code: ABCD-1234 │
│ → Authenticated! Token stored in ~/.phony/credentials │
│ │
│ Method 3: Config File │
│ # ~/.phony/config.yaml │
│ api_key: pk_live_xxxxx │
│ default_project: my-project │
│ │
│ Token Storage: │
│ ├── Stored in ~/.phony/credentials (chmod 600) │
│ ├── Encrypted with OS keychain when available │
│ └── Environment variable takes precedence │
│ │
│ CLI Commands: │
│ $ phony login # OAuth flow │
│ $ phony logout # Clear credentials │
│ $ phony whoami # Show current user │
│ $ phony agent run # Run the agent (daemon mode) │
│ $ phony sync start # Start sync job (local data plane) │
│ $ phony sync status # Check sync status │
│ $ phony snapshot list # List snapshots (from catalog) │
│ $ phony snapshot restore <id> # Restore snapshot │
│ $ phony mock start # Local mock API server (no cloud) │
│ │
└─────────────────────────────────────────────────────────────────┘CI/CD Integration
Because the agent is a static binary, "CI integration" means running the data plane on the CI runner itself — the runner connects to the databases it can already reach, and reports status to the control plane.
┌─────────────────────────────────────────────────────────────────┐
│ CI/CD NATIVE INTEGRATION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ GitHub Actions: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ - name: Refresh staging data │ │
│ │ uses: phonycloud/sync-action@v1 │ │
│ │ with: │ │
│ │ project: my-project │ │
│ │ api-key: ${{ secrets.PHONY_API_KEY }} │ │
│ │ subset: 1% │ │
│ │ wait: true │ │
│ │ # Runs the phony agent ON the runner — data flows │ │
│ │ # runner→DB, only status flows to the control plane │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ GitLab CI: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ refresh_test_data: │ │
│ │ image: phonycloud/agent:latest │ │
│ │ script: │ │
│ │ - phony sync start --project $PROJECT --wait │ │
│ │ variables: │ │
│ │ PHONY_API_KEY: $PHONY_API_KEY │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Generic (any CI): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ # Install the agent binary │ │
│ │ curl -fsSL https://phony.cloud/install.sh | bash │ │
│ │ │ │
│ │ # Or use Docker │ │
│ │ docker run phonycloud/agent sync start --project X │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Ephemeral environments per CI run (never metered): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ phony env spawn --from snap_abc123 --ttl 2h │ │
│ │ # fresh DB from an anonymized snapshot, per test run │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Supported Platforms: │
│ ├── GitHub Actions (native action) │
│ ├── GitLab CI (Docker image) │
│ ├── Bitbucket Pipelines (Docker image) │
│ ├── Jenkins (CLI or Docker) │
│ ├── CircleCI (Orb planned) │
│ └── Azure DevOps (CLI or Docker) │
│ │
│ Webhook Triggers: │
│ ├── On sync complete → trigger test suite │
│ ├── On sync fail → alert Slack/Teams │
│ └── On snapshot create → notify team │
│ │
└─────────────────────────────────────────────────────────────────┘Disaster Recovery & Backup
The blast radius of a control-plane outage is deliberately small: the data plane keeps working (the CLI drives the agent directly), and no customer data is at risk because none is hosted.
┌─────────────────────────────────────────────────────────────────┐
│ DISASTER RECOVERY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Control-plane data (metadata store): │
│ ├── Daily automated backups │
│ ├── Point-in-time recovery (7 days) │
│ ├── Cross-region replication (Enterprise) │
│ └── RTO: 4 hours, RPO: 1 hour │
│ │
│ During a control-plane outage: │
│ ├── Agents keep running scheduled jobs from last-known config │
│ ├── CLI drives the agent directly (sync, snapshot, restore) │
│ ├── Local mock server unaffected │
│ └── Only dashboards, scheduling changes, and reports pause │
│ │
│ Customer data (never hosted — nothing for Phony to lose): │
│ ├── Snapshots: in customer storage, customer bucket policy │
│ ├── Models: in customer infra, exportable .ngram files │
│ ├── Schemas & sync configs: JSON export/import │
│ └── No vendor lock-in: OSS engine uses exported models │
│ │
│ Incident Response: │
│ ├── Status page: status.phony.cloud │
│ ├── Incident notification via email │
│ ├── Post-incident reports (Enterprise) │
│ └── Scheduled maintenance windows (announced 48h ahead) │
│ │
│ SLA Guarantees (control plane + hosted Mock API): │
│ ├── Free/Starter: Best effort │
│ ├── Team: 99.5% uptime │
│ ├── Business: 99.9% uptime │
│ └── Enterprise: self-hosted — your SLA is your ops │
│ │
└─────────────────────────────────────────────────────────────────┘Team & Collaboration Features
RBAC, SSO, and audit are control-plane features — they govern who can configure and trigger data-plane work, not the data itself.
┌─────────────────────────────────────────────────────────────────┐
│ TEAM COLLABORATION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Roles: │
│ ├── Owner - Full access, billing, delete org │
│ ├── Admin - Manage members + agents, all project access │
│ ├── Developer - Create/edit projects, run syncs │
│ └── Viewer - Read-only access, view logs & reports │
│ │
│ Permissions Matrix: │
│ ┌────────────────┬───────┬───────┬───────┬────────┐ │
│ │ Action │ Owner │ Admin │ Dev │ Viewer │ │
│ ├────────────────┼───────┼───────┼───────┼────────┤ │
│ │ View projects │ ✓ │ ✓ │ ✓ │ ✓ │ │
│ │ Create project │ ✓ │ ✓ │ ✓ │ ✗ │ │
│ │ Run sync │ ✓ │ ✓ │ ✓ │ ✗ │ │
│ │ Edit schema │ ✓ │ ✓ │ ✓ │ ✗ │ │
│ │ Register agent │ ✓ │ ✓ │ ✗ │ ✗ │ │
│ │ Manage members │ ✓ │ ✓ │ ✗ │ ✗ │ │
│ │ Billing │ ✓ │ ✗ │ ✗ │ ✗ │ │
│ │ Delete org │ ✓ │ ✗ │ ✗ │ ✗ │ │
│ └────────────────┴───────┴───────┴───────┴────────┘ │
│ │
│ Audit Logging: │
│ ├── Who did what, when (incl. agent registrations/revocations) │
│ ├── IP address and user agent │
│ ├── Searchable and exportable │
│ └── Retention: 90 days (Business: 1 year) │
│ │
└─────────────────────────────────────────────────────────────────┘