Custom API instances
The official Pinchana Web client accepts a custom API only when the exact origin has a valid, unexpired Ed25519 certificate issued by the Pinchana project.
This is a security boundary, not a cosmetic verification badge. Without it, the Web server’s same-origin /api/... routes could be pointed at arbitrary user-supplied servers.
Certificate envelope
Section titled “Certificate envelope”An approved API publishes the public envelope from GET /web/identity:
{ "payload": "BASE64URL_JSON_CLAIMS", "signature": "BASE64URL_ED25519_SIGNATURE"}The certificate binds:
- issuer and certificate protocol version;
- the exact normalized API origin;
- the public Cloudflare Turnstile site key used by the Web hostname;
- issue time;
- expiry time.
Production custom origins must use HTTPS and must not include credentials, paths, query strings, or fragments. Local development can permit HTTP origins.
Connection flow
Section titled “Connection flow”- The user enters an API origin in Web Settings.
- Browser code fetches that origin’s
/web/identitypublic envelope. - The origin and envelope are submitted to same-origin
POST /api/instance. - The Web server verifies the Ed25519 signature with server-only
PINCHANA_INSTANCE_PUBLIC_KEYand validates the claims, exact origin, and time bounds. - The certificate is stored in the HttpOnly
pinchana_instancecookie until its expiry. - The previous Web session is cleared.
- Turnstile and future API calls use the certified origin/site-key pair.
The custom API URL is not persisted as an arbitrary local-storage string. Server-side code derives the trusted origin from the verified certificate.
API operator installation
Section titled “API operator installation”Instance operators receive the certificate envelope, never the project signing key. The simplest Compose configuration is a quoted one-line JSON value:
PINCHANA_INSTANCE_CERTIFICATE='{"payload":"BASE64URL_CLAIMS","signature":"BASE64URL_SIGNATURE"}'PINCHANA_INSTANCE_CERTIFICATE_FILE=The same API deployment needs its private Turnstile configuration:
TURNSTILE_SECRET_KEY=REPLACE_WITH_PRIVATE_TURNSTILE_SECRETTURNSTILE_EXPECTED_HOSTNAME=pinchana.example.comTURNSTILE_EXPECTED_ACTION=turnstile-spin-v1TURNSTILE_SESSION_SECRET=REPLACE_WITH_AT_LEAST_32_RANDOM_CHARACTERSThe certificate contains the public Turnstile site key. It must never contain the private Siteverify secret.
Recreate the gateway and verify the public identity through the final HTTPS origin:
docker compose --env-file .env up --detach --force-recreate servercurl --fail-with-body --silent --show-error https://api.example.com/web/identityA 503 means no certificate source reached the running server container.
File-based certificate
Section titled “File-based certificate”For secret-management systems that prefer files, mount the public envelope read-only and set its container path:
services: server: volumes: - ./secrets/instance-certificate.json:/run/secrets/pinchana-instance-certificate.json:ro environment: PINCHANA_INSTANCE_CERTIFICATE_FILE: /run/secrets/pinchana-instance-certificate.jsonLeave PINCHANA_INSTANCE_CERTIFICATE empty when using the file. The inline value takes precedence when both are set.
Issuance and renewal
Section titled “Issuance and renewal”The Ed25519 private key stays offline, outside all public repositories and normal API hosts. Maintainers can use the API repository’s signing tool after reviewing an instance:
node scripts/sign-instance-certificate.mjs \ /secure/pinchana-instance-private.pem \ https://api.example.com \ REPLACE_WITH_PUBLIC_TURNSTILE_SITE_KEY \ 90The final argument is validity in days. The signing tool supports 1 through 366 days.
Issue a replacement before expiry, replace the installed envelope, and recreate server. Expired certificates are rejected; users must select the renewed instance again.
What the certificate proves
Section titled “What the certificate proves”It proves that the Pinchana project authorized a specific API origin and Turnstile configuration when the certificate was issued. It prevents a copied certificate from being valid on a different origin.
It does not continuously prove that a third-party host still runs unmodified public code. Continuous software-state guarantees would require hardware-backed measured boot/remote attestation. Keep certificates short-lived and refuse renewal when an operator is no longer trusted.