Skip to content

System Architecture

Derived from technical/architecture-overview.md and technical/security-architecture.md (both last updated 2026-06-14). Production runtime is GCP partile-prod / europe-west2, fronted by Cloudflare, with LinkedIn as the identity provider. There is no real user data yet — pre-launch seed data is tagged mock data that must be purged before GA.

Production topology

flowchart LR
  %% source: technical/architecture-overview.md (2026-06-14)
  subgraph clients["Clients"]
    ios["Native iOS app (Swift) — in migration"]
    rn["React Native app — running v1"]
  end

  subgraph edge["Cloudflare edge"]
    worker["Worker: api.partile.app<br/>origin host rewrite"]
    pages["Pages + Access<br/>data-room publication (gated)"]
  end

  subgraph gcp["GCP partile-prod (europe-west2)"]
    subgraph run["Cloud Run"]
      api["API service (FastAPI)<br/>public ingress, app-level auth"]
      jobs["Jobs: matching / retention / migrate"]
      responder["remediation-responder<br/>private, SHADOW (ENFORCE=false)"]
    end
    subgraph vpc["Private VPC (not publicly reachable)"]
      sql[("Cloud SQL PostgreSQL 16<br/>private IP, backups + PITR")]
      redis[("Memorystore Redis<br/>OAuth state, handoff codes, rate limits")]
    end
    scheduler["Cloud Scheduler"]
    secrets["Secret Manager<br/>(6 secret labels; values never in git)"]
    monitoring["Cloud Monitoring<br/>uptime + 5xx alert policies"]
    pubsub["Pub/Sub<br/>partile-prod-remediation-alerts"]
  end

  linkedin["LinkedIn OIDC<br/>signed id_token via JWKS"]
  anthropic["Anthropic Claude<br/>key-gated, minimized prompts"]
  audit["Structured JSON audit log"]

  ios -->|"HTTPS, Bearer opaque session"| worker
  rn -->|"HTTPS, Bearer opaque session"| worker
  worker -->|"HTTPS"| api
  api -->|"SQL over private IP/socket"| sql
  api -->|"hashed lookups"| redis
  api -->|"HTTPS"| linkedin
  api -->|"HTTPS"| anthropic
  scheduler -->|"OAuth (dedicated SA)"| jobs
  jobs --> sql
  jobs --> redis
  secrets -.->|"per-secret accessor IAM"| api
  secrets -.-> jobs
  monitoring --> pubsub
  pubsub -->|"push POST /pubsub"| responder
  responder --> audit

Key properties (all evidenced in the control register):

  • Data-plane isolation (SEC-08): Cloud SQL and Redis are private-network only; the only public surface is the consumer API, protected by LinkedIn OIDC sessions and SQL-enforced object authorization (SEC-04).
  • Least-privilege IAM (SEC-16): dedicated API, worker, scheduler, and responder service accounts; the responder has read-only observability roles plus run.developer scoped to the API service only.
  • Shadow remediation (OPS-06): the responder is default-deny, allowlists two runbook paths, is circuit-breaker bounded, and — with ENFORCE=false — audits decisions without ever mutating production.
  • Accepted risk (GOV-07): the API's public allUsers invoker binding is a documented, project-scoped exception tracked in the action register.

Authentication flow (LinkedIn OIDC → mobile session)

sequenceDiagram
  %% source: technical/security-architecture.md (2026-06-14)
  autonumber
  participant M as Mobile app
  participant API as Cloud Run API
  participant R as Redis
  participant L as LinkedIn OIDC

  M->>API: Start sign-in
  API->>R: Store single-use CSRF state (short TTL)
  API-->>M: Fixed LinkedIn redirect (no open redirect)
  M->>L: User authenticates and consents
  L-->>API: Callback with code + state
  API->>R: Atomic GETDEL consume of state
  API->>L: Exchange code and fetch JWKS
  API->>API: Verify signed id_token (alg, kid, iss, aud, exp, sub)
  API->>R: Store one-time handoff code (120s TTL)
  M->>API: Exchange handoff code (never a token in a URL)
  API->>API: Issue opaque session token, store SHA-256 hash only
  API-->>M: Session token (Keychain on native iOS)

OAuth access/ID/refresh tokens are never persisted; session tokens exist in the database only as hashes (SEC-01/02/03/05/10).

Trust boundaries

# Boundary Protection
1 Mobile → API edge TLS; opaque Bearer sessions; 401/429 handling
2 Cloudflare → Cloud Run HTTPS to managed-TLS origin (token rotation is a tracked open action)
3 Cloud Run → data stores Serverless VPC connector; private IP/socket; no public reachability
4 API/jobs → LinkedIn / Anthropic HTTPS; signed-token verification; key-gated minimized prompts
5 Scheduler → jobs OAuth from a dedicated service account
6 Operator/deploy → GCP Terraform, GCS remote state, Secret Manager (Tier 2)
7 Monitoring → responder Pub/Sub push to a private service; shadow-only
8 Control-plane VM → GCP Keyless VM-attached service account (per-agent isolation is a tracked caveat)

Full boundary table and open gaps: technical/architecture-overview.md.