# Deploy v24 hosted Python libraries on Render

Your current static website continues to serve the tools and owner workspace. A new Python web service executes hosted functions and saves owner libraries. You can use the same GitHub repository for both services.

## 1. Update GitHub and the static site

1. Extract the complete `petrophysics-io-v24-render.zip`, or the v23-to-v24 update ZIP for the exact supplied v23 release.
2. Upload the contents of the inner `petrophysics-io` folder to the existing repository on its Render-connected branch. Preserve folders and replace the supplied files. Do not upload the ZIP itself or add another enclosing project directory.
3. Commit, for example: `v24: hosted Python libraries and external function API`.
4. In Render, open the existing static site's settings. Set **Build Command** to `node scripts/build-static.mjs` and **Publish Directory** to `dist`. Keep the existing site/domain connection. Publishing `.` would expose service files; the v24 build copies only public website assets.
5. Deploy the commit. Keep any existing GPT and partner backend services and their secrets in place.

## 2. Create the Python service

In Render choose **New → Web Service**, select the same repository and branch, and enter:

| Setting | Value |
|---|---|
| Service name | `petrophysics-library-api` or your chosen name |
| Language / runtime | Python 3 |
| Root directory | `library-server` |
| Build command | `pip install -r requirements.txt` |
| Start command | `uvicorn pio_library_server:app --host 0.0.0.0 --port $PORT --workers 1` |
| Health check path | `/health` |
| Instances | 1 |
| Plan | A paid web service that supports a persistent disk |

Set these environment variables in that service:

| Variable | Value |
|---|---|
| `PYTHON_VERSION` | `3.12.12` |
| `LIBRARY_ADMIN_PASSWORD` | A unique, random 32–256 ASCII-character password without spaces |
| `LIBRARY_DATA_DIR` | `/var/data/pio-libraries` |
| `LIBRARY_ALLOWED_ORIGINS` | `https://petrophysics.io,https://www.petrophysics.io` |
| `LIBRARY_MEMORY_MB` | `256` |
| `LIBRARY_MAX_CONCURRENT` | `1` |

The owner password belongs only in Render's backend environment and the owner's current authenticated browser session. Do not write it in configuration JavaScript, documentation, a repository or a public client.

Start with one concurrent call and a 256 MiB worker limit for small computations. The memory limit constrains a worker's virtual address space; it does not reserve physical RAM or guarantee that the container can accommodate every calculation. For larger NumPy/SciPy/pandas computations, upgrade the service's memory plan and adjust `LIBRARY_MEMORY_MB` together. Raise concurrency only when that plan has capacity for the API process plus all simultaneous workers.

In **Disks**, attach a disk with **Mount Path** `/var/data`; a 1 GB disk is a reasonable starting configuration for small code modules. This is required for saved libraries, versions and key records to survive restarts/redeploys. The application writes its storage under `/var/data/pio-libraries`. Data outside the mounted directory remains temporary. A disk belongs to a single service instance; keep one instance and one API worker for v24's storage/execution coordination. Expect a brief API interruption during a disk-backed redeploy.

Deploy the service and record its assigned URL, for example `https://petrophysics-library-api-xxxx.onrender.com`. Open `/health` and `/api/v1/libraries`; the latter should list `demo`. No owner password is required for the public catalogue. `/health` reports the configured storage location; it cannot prove Render has mounted a durable disk. Verify the disk in Render and use the controlled restart check below.

## 3. Connect the workspace

Edit `js/library-config.js` in GitHub so its API URL exactly matches your deployed HTTPS service URL. Deploy the static site again. This allows the hosted library workspace on `petrophysics.io` to reach the Python service. The allowed origins above are browser website origins, without a trailing slash.

The default `https://api.petrophysics.io` is a target hostname, not an automatically created service. Until it is configured, use your actual `onrender.com` URL in the config and in the Python client.

## 4. Optional: use api.petrophysics.io

1. Open the new Python web service's **Settings → Custom Domains** and add `api.petrophysics.io`.
2. In Porkbun's DNS settings for `petrophysics.io`, add the record Render instructs you to use for the `api` subdomain, normally a CNAME to the service's `onrender.com` hostname. Follow the service's exact displayed instructions.
3. Return to Render and verify the domain. Wait until verification and HTTPS are working.
4. Change `js/library-config.js` to `https://api.petrophysics.io`, deploy the static site, and give that URL to external Python callers.

Render manages HTTPS certificates for verified custom domains. Do not alter the existing homepage's DNS records while configuring this separate API subdomain.

## 5. Verify and publish your own library

1. Refresh the website with Ctrl+Shift+R and open **Hosted libraries**.
2. Load the public catalogue and test `demo.vsh` with `[20, 60, 100]`; version 1 returns `[0.0, 0.5, 1.0]` using its default endpoints.
3. Download `downloads/petrophysics_client.py`, put it beside your script and run:

```python
from petrophysics_client import Client

api = Client("https://YOUR-DEPLOYED-SERVICE.onrender.com")
print(api.library("demo", version=1).vsh([20, 60, 100]))
```

4. Sign in as owner, upload a reviewed `.py` module or write it online, choose exported functions, and save a draft. Publish it with public or key access when ready.
5. For key access, securely copy the displayed consumer key and share that key with the authorized caller. Never share `LIBRARY_ADMIN_PASSWORD`.
6. Pin a published version in the caller's script. Editing creates a new version; existing workflows can use their pinned published version while the library remains published and authorized.
7. After a controlled restart, check that your saved library still appears. Maintain your own source backup and follow Render's disk backup guidance for backend data.

Private business module source must be uploaded through the authenticated library workspace, which writes to the backend disk. Do not add the business module to `downloads/`, any static asset directory, or the GitHub repository. Only the generic client wrapper is intended as a public download.

## Troubleshooting

| Symptom | Check |
|---|---|
| Catalogue cannot connect | Correct HTTPS API URL in `js/library-config.js`; Python service live; `/health` responds |
| Browser access blocked | Exact website origin present in `LIBRARY_ALLOWED_ORIGINS`, including `www` if used |
| Owner sign-in rejected | Enter the Python service's `LIBRARY_ADMIN_PASSWORD`, distinct from existing partner admin secrets |
| Saved libraries disappear | Disk attached at `/var/data`; `LIBRARY_DATA_DIR=/var/data/pio-libraries`; service writing to that directory |
| Function not callable | Library published, function exported, selected version previously published, valid consumer key if required |
| Python import rejected or missing | Supported dependency included in backend `requirements.txt` and redeployed; browser package loading is separate |
| Request times out | Reduce batch size/work per request and inspect service logs; do not blindly retry a function with side effects |

Only reviewed owner code is supported. Source checks and worker limits do not make this a full sandbox for untrusted third-party uploads. Existing well data stays in the browser unless explicitly supplied in a remote call, which sends that JSON to the backend.

## Official references

- [Render's FastAPI deployment guide](https://render.com/docs/deploy-fastapi): Python build and Uvicorn service command.
- [Render persistent disks](https://render.com/docs/disks): paid services, mounted storage, persistence and single-instance limits.
- [Render custom domains](https://render.com/docs/custom-domains): domain addition, DNS, verification and HTTPS.
