Orkestia
Blog
App Data

PostgREST HTTP

Point a standard PostgREST client at App Data using an Orkestia end-user JWT and the public JWKS — no DSN, no minted HS256 secret.

Most apps should use the Data API / MCP (discover → describe → read / create / update / delete / call). That surface never speaks SQL and never asks you to hold a database secret.

This page is the other door: standard PostgREST HTTP. It exists so an existing PostgREST client (or a binary you already run) can talk to App Data with config only. You still never receive a Postgres DSN. Isolation is still RLS on the serving database.

This path is rolling out. Until the dual JWKS file is live in production, the public hostname still mints a short HS256 token for PostgREST. The JWT claims below are already on newly issued end-user tokens; verification against PostgREST's jwt-secret follows the JWKS sidecar.

When to use which

You want…Use
An agent / MCP client that must not invent SQLData API at https://appdata-mcp.orkestia.dev
A PostgREST client you already have (POSTGREST_URL + JWKS)This page

Do not mint a 120-second HS256 yourself. Do not invent tenant_id. Do not send a Cognito / org-member token as if it were an end-user. A 401 or 403 from /rest/v1 fails closed — there is no privileged fallback that retries as appdata_owner_*.

URL and JWKS

POSTGREST_URL=https://appdata.orkestia.dev/rest/v1

Verify the end-user JWT against the public JWKS (RS256, kid prod-1 today):

https://workflow-api.orkestia.dev/api/auth/end-user/jwks

The same key set is republished at https://appdata.orkestia.dev/.well-known/jwks.json. https://api.orkestia.dev/api/auth/end-user/jwks is 404 — that host is api-core, not the IdP. https://login.orkestia.dev/.well-known/jwks.json currently serves the SPA HTML; use the workflow-api URL until that host publishes keys.

Issuer is https://login.orkestia.dev. Do not set PGRST_JWT_AUD on a client that shares one PostgREST with every app: aud is the app's client_key.

Catalog for v1 is PostgREST OpenAPI (follow-privileges). There is no shared mcp_catalog schema on the serving database. Set the schema to app_<hex> (see claims), not public.

Token

Sign-in is the existing PKCE / /token flow (@orkestia/auth or your own). Access TTL is one hour with a rotating refresh token.

The access token keeps the IdP fields (iss, sub, aud, org, app, user_type, email, iat, exp, jti) and adds:

ClaimValue
roleappdata_r_ + UUID hex of the identity app (no dashes)
app_schemaapp_ + the same hex
end_user_uuidthat app's EndUser.uuid (not an alias of sub forever)
identity_app_uuidsame as app
app_organization_uuidoptional in-app workspace. Omit the key when unbound. Never null.

org is the paid platform organization that owns the Identity App. It is not the workspace. Bind a workspace by sending app_organization_uuid on POST /api/auth/end-user/token (authorization-code and refresh). The server checks membership and 403s workspace_forbidden if the user is not in that workspace. Do not implicit-bind when the user has exactly one workspace. Never put appdata_owner_* or tenant_id on an end-user token.

Owner-mode tables filter on end_user_uuid from request.jwt.claims. A token that only has IdP sub / app fails closed.

Accept-Profile

Send Accept-Profile: app_<hex> (and Content-Profile on writes) matching app_schema. A mismatch is refused.

Workspace header (Data API only)

The MCP / Data API gateway still accepts X-App-Organization-Uuid / X-Orkestia-Workspace for Path A. Path B binds the workspace in the token so a process that only holds the JWT does not need that header.

Records & Data API

The SQL-less MCP surface.

Ownership

Owner, app, and workspace rows.

Auth SDK

PKCE, refresh, and the end-user JWT.