Skip to content

HTTP reference

This page lists what we put on the wire between a caller and your app: the addresses, the headers, the identity token and the answers the edge gives. The guides linked from each section explain them.

To script Jiayang Cloud itself (deploy, share, read the audit log), use the API clients for Node and Python, or the CLI with --json.

  • https://<app>--<workspace>.jiayang-apps.cloud is for people in a browser. They prove who they are with a session cookie for that one host, set after they sign in.
  • https://jiayang-apps.cloud/<workspace>/<app>/<path> is for scripts and machines. They send Authorization: Bearer <token>, with a bypass token or a CLI session.
  • The same address on a public path is for webhook senders. They send none of our credentials, only the provider’s signature on a verified path.

On the apps domain, your app gets the request without /<workspace>/<app>. So https://jiayang-apps.cloud/acme/hello/api/items reaches your app as /api/items.

A bearer token sent to the app’s own address gets 401 unauthorized. That address takes only a browser sign-in. See Tokens for scripts and machines and Webhooks and public paths.

Header What it holds
X-Jiayang-Identity The identity token: a JWT signed with RS256, made out to your app, valid for 60 seconds. Missing on a public path with no verifier.
X-Jiayang-Email The caller’s email, for display only. Missing for a bypass token and a webhook delivery. Don’t decide anything from it.
X-Forwarded-For The caller’s IP address, and only that address, as Cloudflare saw it.
X-Forwarded-Proto The scheme the request came in on.

Before it sets these, the edge deletes every header the client sent whose name starts with X-Jiayang-, Cf-Access- or Cf-Container-, with dashes or underscores. It also removes what the client sent in Forwarded, X-Forwarded-Host, X-Real-IP, True-Client-IP and the other headers a proxy uses to say who called.

The edge also removes our session cookie from Cookie, and removes Authorization when it carried one of our tokens. See Security.

Claim Value
iss https://edge.jiayang.cloud
aud Your app’s id. For a webhook delivery, webhook:<app id>.
sub The caller’s stable id: usr_… for a person, service:<token id> for a bypass token, webhook:<verifier id> for a delivery
kind user, service or webhook
email The person’s email. Absent for a token and a delivery.
role viewer, editor or owner on this app. Absent for a delivery.
wid The app’s workspace id
iat, exp When it was made, and 60 seconds later
jti A random id for this token

A webhook delivery’s token also has provider, pattern, and delivery and signed_at where the provider signs them. Its header’s typ is webhook+jwt.

The public keys are at https://edge.jiayang.cloud/.well-known/jwks.json. The SDK overview lists every check, and Without an SDK shows how to make them yourself.

Name Value Where
JIAYANG_APP_ID Your app’s id, the audience its tokens carry Every app
JIAYANG_IDENTITY_ISSUER https://edge.jiayang.cloud Every app
JIAYANG_JWKS_URL https://edge.jiayang.cloud/.well-known/jwks.json Every app
DB The app’s D1 database Worker apps. See Databases.
PORT The port the container listens on Container apps. See Containers.

A Worker gets these on env. A container gets them as environment variables. You can’t set names we use. See Environment variables.

  • It removes the Domain attribute from every cookie your app sets, and drops a cookie named like one of ours.
  • It marks every response Cache-Control: private, and removes headers that tell a CDN to cache it. A max-age your app sends is kept.
  • It puts no-transform first in Cache-Control, on your app’s responses and on the edge’s own. That asks Cloudflare and any proxy that follows the standard to leave the body as it is. The Cloudflare features that don’t read it, Rocket Loader and Automatic HTTPS Rewrites, are switched off for the apps domain. The same goes for your own domain, unless its DNS is on Cloudflare and proxies the record to us: then your zone’s settings apply first, and what you turn on there can change your pages. Nothing rewrites your page’s http:// links to https://, so a script, style sheet or image your page loads from an http:// address is blocked by the browser as mixed content. Link to https:// addresses, or to paths on your own app. A proxy that ignores no-transform can still change the body. Text responses (HTML, CSS, JavaScript, JSON, SVG and the like) your app sends without a Content-Encoding are compressed with br or gzip when the browser accepts it, with Vary: Accept-Encoding added and a strong ETag made weak. Event streams, ranges, anything already encoded and a response whose Content-Length is over 4 MiB go out as they came. So does a response your app is still writing: the edge waits up to 250ms for the first part of the body, and if the body hasn’t ended or passed 64 KiB within 20ms of that, the response goes out uncompressed, so each part reaches the browser as soon as it’s written. A page your app writes in parts over more than 20ms goes out uncompressed too, and so does one with no Content-Length that passes 64 KiB within those 20ms, since nothing says how large it will get. Send a Content-Length to have a page over 64 KiB compressed. Send X-Accel-Buffering: no to skip compression for a response outright.
  • Over HTTPS, every response carries Strict-Transport-Security: max-age=63072000; includeSubDomains.
  • Every response carries Content-Security-Policy: frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN, so only your app’s own pages can show it in a frame. A Content-Security-Policy your app sends is kept, and ours is added beside it as a second policy. A browser enforces both, so your app can make framing stricter, such as frame-ancestors 'none', but not looser. Your app’s X-Frame-Options: DENY is kept; any other value becomes SAMEORIGIN. No other app can show yours in a frame, one in the same workspace included, and neither can any other site.
  • On the apps domain, responses carry Content-Security-Policy: sandbox, can’t be shown in a frame at all, and can’t set cookies. A redirect to the app’s own address, like Location: /login or Location: https://<app>--<workspace>.jiayang-apps.cloud/login, is rewritten to stay under /<workspace>/<app>, and so is one relative to the page, like Location: ../login. One to https://jiayang-apps.cloud/<workspace>/<app>/..., the app’s own prefix written out, is kept. Every other redirect is dropped, another app on the apps domain or anywhere else, because a client would follow it with the caller’s token. Some clients even put the token back on after a hop to another site: older Go sends it to a second page on that site, and curl sends it again when that site sends it back to the apps domain, at another app. The status goes out without a Location. To send a machine somewhere else, such as a signed download link, return the URL in the response body. Browsers on your app’s own address get every redirect as your app wrote it.

These come from the edge, before or instead of your app:

Status When
302 A page load with no session. It goes to sign in, then back.
401 unauthorized No valid session or token, or a return from sign-in with a code that doesn’t work, at any address. On a public path, also every failed check.
402 The workspace is paused: a Free workspace used what its plan includes this month, a paid one reached its usage limit, or a container app is on Free.
403 forbidden A write from another site, another site opening the app in a frame, a bypass token for another app, a suspended workspace, or an app we took down.
404 not found At an app’s own address: someone the app isn’t shared with, a member of its workspace included, or no app there. The two look the same. App names are unique in a workspace, so a member who tries to make one with a name in use is told the name is in use.
413 payload too large A delivery to a verified public path with a body over 1 MiB.
429 too many requests Too many requests to public paths, or to sign-in.
503 service unavailable The edge couldn’t check access, sign a token, or write the audit log, so it refused. An app also gets it while we put back a version deployed by someone who could no longer edit it.
507 The workspace’s databases are over the plan’s limit.

Each refusal is in the audit log with its reason. How access works has the pages a browser sees.

/.well-known/security.txt on your app’s address is the platform’s: it names where to report a security problem or abuse, and your app never gets that path.