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.
Addresses
Section titled “Addresses”https://<app>--<workspace>.jiayang-apps.cloudis 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 sendAuthorization: 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.
Headers your app receives
Section titled “Headers your app receives”| 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.
The identity token
Section titled “The identity token”| 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.
Variables we set
Section titled “Variables we set”| 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.
What the edge changes in responses
Section titled “What the edge changes in responses”- It removes the
Domainattribute 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. Amax-ageyour app sends is kept. - It puts
no-transformfirst inCache-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’shttp://links tohttps://, so a script, style sheet or image your page loads from anhttp://address is blocked by the browser as mixed content. Link tohttps://addresses, or to paths on your own app. A proxy that ignoresno-transformcan still change the body. Text responses (HTML, CSS, JavaScript, JSON, SVG and the like) your app sends without aContent-Encodingare compressed withbrorgzipwhen the browser accepts it, withVary: Accept-Encodingadded and a strongETagmade weak. Event streams, ranges, anything already encoded and a response whoseContent-Lengthis 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 noContent-Lengththat passes 64 KiB within those 20ms, since nothing says how large it will get. Send aContent-Lengthto have a page over 64 KiB compressed. SendX-Accel-Buffering: noto 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'andX-Frame-Options: SAMEORIGIN, so only your app’s own pages can show it in a frame. AContent-Security-Policyyour 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 asframe-ancestors 'none', but not looser. Your app’sX-Frame-Options: DENYis kept; any other value becomesSAMEORIGIN. 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, likeLocation: /loginorLocation: https://<app>--<workspace>.jiayang-apps.cloud/login, is rewritten to stay under/<workspace>/<app>, and so is one relative to the page, likeLocation: ../login. One tohttps://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 aLocation. 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.
Answers from the edge
Section titled “Answers from the edge”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.
Next steps
Section titled “Next steps”- How access works: one request, step by step.
- SDK overview: verify the identity token in your app.