# PF Simulation SaaS: external integrations The hosted edition supplies real provider adapters with explicit configuration. The release tests their request/response contracts using controlled doubles. It has not provisioned AWS resources, sent an external email, authenticated a real Cognito account or processed a live payment. Those checks require the operator's configured accounts. The standalone scientific edition imports none of these adapters and continues to run without a SaaS account. ## Private results and input storage `PF_SAAS_STORAGE_BACKEND=local` uses a private directory outside the web document root. Local storage is designed for Linux/POSIX; Windows users can run the SaaS edition through the supplied Linux containers. Every key is a relative POSIX path. Traversal, absolute paths, control characters and symlinks are rejected. Files are written atomically with owner-only permissions. SaaS services authorize the organization and project before opening a stored object. `PF_SAAS_STORAGE_BACKEND=s3` uses `PF_SAAS_S3_BUCKET`, `PF_SAAS_S3_PREFIX` and `PF_SAAS_AWS_REGION`. Optional `PF_SAAS_S3_KMS_KEY` selects KMS encryption; otherwise uploads request SSE-S3. The service obtains credentials from the standard AWS credential chain, including the task's IAM role. Do not place access keys in browser configuration. The AWS deployment configures private buckets and block-public-access settings. Storage adapters do not assign public ACLs or return anonymous object links; downloads pass through the application's authorization checks. The stored numerical artifacts retain complete complex density matrices and provenance. Cloud storage does not recalculate or simplify scientific results. ## Accounts and email Local accounts use verified email addresses and server-side sessions. In `PF_SAAS_MAIL_MODE=outbox`, messages are written to `data_dir/outbox` for a local developer or operator to inspect. The response says `delivered: false`; the application does not pretend to send them. This mode is refused in production. Outbox files contain one-time account links and must remain private. For actual SMTP delivery, select `PF_SAAS_MAIL_MODE=smtp` and configure `PF_SAAS_SMTP_HOST`, `PF_SAAS_SMTP_PORT`, `PF_SAAS_SMTP_FROM`, and any required `PF_SAAS_SMTP_USERNAME` and `PF_SAAS_SMTP_PASSWORD`. Choose STARTTLS with `PF_SAAS_SMTP_STARTTLS=true` or implicit TLS with `PF_SAAS_SMTP_SSL=true` and STARTTLS false. Production requires encrypted SMTP transport. A successful SMTP response means the mail server accepted the message; recipient delivery still depends on the mail provider and domain configuration. Optional Cognito mode uses `PF_SAAS_AUTH_MODE=cognito`, `PF_SAAS_COGNITO_REGION`, `PF_SAAS_COGNITO_USER_POOL_ID`, `PF_SAAS_COGNITO_CLIENT_ID` and the hosted login domain `PF_SAAS_COGNITO_DOMAIN`. The server checks the RS256 signature against the configured pool's JWKS, exact issuer and client audience, expiration, issued-at, subject, `token_use=id`, and verified email. Provider identities are keyed by issuer and subject. Possession of an email string does not authorize linking an existing local account. The account module supplies the browser authorization code/PKCE flow and exchanges the verified identity for the application's HttpOnly session cookie. ## Subscription plans and entitlements Billing is disabled by default. Accounts use the configured Starter limits; the UI does not invent subscription prices or a payment connection. Set `PF_SAAS_BILLING_MODE=stripe`, `PF_SAAS_STRIPE_SECRET_KEY`, `PF_SAAS_STRIPE_WEBHOOK_SECRET`, `PF_SAAS_STRIPE_PRICE_RESEARCH` and `PF_SAAS_STRIPE_PRICE_TEAM` to enable the adapter. The two configured price IDs must be distinct. Create the corresponding recurring prices and configure the Stripe customer portal in the provider account before offering paid checkout. Prices, currency, tax behavior, trial policy and payment methods belong to that provider configuration. No browser-supplied price ID or payment amount is used. The initial server-owned quota policy is: | Plan | Per-job time | Concurrent jobs | Queued jobs | Monthly compute | Storage | |---|---:|---:|---:|---:|---:| | Starter | Base | Base | Base | Base | Base | | Research | Base | 2 × base | 2 × base | 4 × base | 4 × base | | Team | Base | 4 × base | 4 × base | 12 × base | 12 × base | Base values are the deployment's `max_job_seconds`, `max_concurrent_jobs`, `max_queued_jobs`, `monthly_compute_seconds`, and `max_storage_bytes` settings. The API and worker must use matching base settings. The operator should choose these allowances to match measured capacity and the published commercial offer. They limit admitted work; they never reduce quantum fidelity or generate substitute simulation results. Worker resources are controlled separately by the fixed task/container definition. Configure the webhook at `/api/billing/webhook` for `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`, `checkout.session.completed`, `checkout.session.expired`, and `checkout.session.async_payment_succeeded`. It verifies an HMAC over the raw body and a timestamp within five minutes, records processed event IDs, and retrieves current subscriptions before changing entitlements. Repeated or out-of-order event payloads cannot alone restore a canceled subscription. Provider outages return an error so delivery can retry. Monitor failed webhook deliveries; an administrator can request reconciliation through the endpoint below after restoring the connection. Active/trialing subscriptions receive the matching plan. Past-due, unpaid, paused or invalid subscription states block new compute admission. Cancellation returns to Starter limits while existing authorized results remain accessible. An administrator may grant a plan separately, with a recorded reason. Such a grant reports `source=administrator`, `status=manual`; it is not recorded or presented as a paid subscription and can be removed explicitly. | Endpoint | Required access | Purpose | |---|---|---| | `GET /api/billing/plans` | Signed in | Plans, quota policy and checkout availability | | `GET /api/billing?org_id=…` | Organization viewer | Effective plan, limits and integration status | | `POST /api/billing/checkout` | Organization admin/owner, CSRF | `{org_id, plan_id}` → hosted checkout URL | | `POST /api/billing/portal` | Organization admin/owner, CSRF | `{org_id}` → hosted billing portal URL | | `POST /api/billing/webhook` | Valid provider signature | Idempotent subscription synchronization | | `PATCH /api/admin/organizations/{id}/entitlements` | Platform admin, CSRF | `{plan_id, reason}`; null removes the override | | `POST /api/admin/organizations/{id}/billing/reconcile` | Platform admin, CSRF | Recover current provider subscription state | Payment credentials are needed only by the API. A worker can keep `PF_SAAS_BILLING_MODE=disabled`: it still enforces the persisted subscription and administrator entitlements without calling Stripe. ## AWS job execution `PF_SAAS_EXECUTION_BACKEND=aws_batch` selects a fixed `PF_SAAS_AWS_BATCH_QUEUE` and `PF_SAAS_AWS_BATCH_JOB_DEFINITION` in `PF_SAAS_AWS_REGION`. The deployment owns the image, task role, database connection, storage permissions and resource allocation. Job submissions can supply only validated application job, attempt and lease identifiers. They cannot replace the image, role, command, queue or environment. The adapter invokes `python -m pf_saas.worker --job-id … --attempt-id … --lease-token …`. Batch automatic retries are disabled; failed attempts are visible and application retries are explicit. The database fences duplicate executors for an attempt. A Batch `SUCCEEDED` status alone does not mark a scientific run successful: the worker must commit its real outputs and finish its application lease. Cancellation terminates running tasks and cancels those that have not started. Continuous tracking is executed as explicitly bounded jobs under the selected resource policy. ## Verification scope and source references `test_saas_cloud.py` covers private local file round trips and traversal/symlink rejection; S3 encryption request parameters; explicit development outbox and SMTP request contracts; Stripe fixed-price checkout, authenticated webhooks, idempotency, out-of-order reconciliation and tenant boundaries; manual grant auditing; RSA Cognito claim rejection; the fixed AWS task command; and billing HTTP authorization and CSRF checks. External adapters are doubled in these tests, not live-connected. Production activation additionally needs actual provider configuration and verification of redirects, mail delivery, webhook delivery, task startup, network access, IAM permissions, storage encryption, recovery and costs. See `SAAS_DEPLOYMENT.md` for the deployment procedure and its separate validation record. Primary interface references: - [Stripe webhook handling](https://docs.stripe.com/webhooks) - [Cognito ID tokens](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html) - [AWS Batch submit_job](https://docs.aws.amazon.com/boto3/latest/reference/services/batch/client/submit_job.html) - [AWS Batch termination behavior](https://docs.aws.amazon.com/batch/latest/APIReference/API_TerminateJob.html)