When someone opens a project-scope service through the access edge, the request reaches your app only after the server has authenticated the person and checked that they hold access to the project. Your app is told who that was. This page is what an app author needs to read that identity — and, when trusting the network is not enough, to verify it.
What arrives with every proxied request
| Header | Value |
|---|---|
X-Orc-User | the user's id |
X-Orc-User-Email | their email address |
X-Orc-Access-Assertion | the same identity as a signed token (below) |
X-Forwarded-Host | the access host the browser used |
X-Forwarded-Proto | always https |
X-Forwarded-For | the client chain, with the server appended |
Host is rewritten to the internal name your service knows itself by, so links your app builds from Host keep working; the server turns an absolute Location pointing back at that internal origin into the access origin on the way out.
Any X-Orc-* header on the incoming request is removed before these are added. A client cannot claim to be someone by typing the header.
A public name served through the edge carries none of these: nobody signed in, so there is nobody to name.
When the headers are enough, and when they are not
The headers are trustworthy exactly as far as your deployment is: the only route to your service's port is through the edge, which is why the port is not published in the first place. If something else can reach that port — a node in the same project, a sidecar, a developer with a forwarded port — then it can send the headers too, and your app cannot tell the difference.
The assertion removes that assumption. It is a compact JWT signed by the server with a key it never shares, so your app can check the signature itself and stop caring what else can reach it.
Verifying the assertion
The token is signed EdDSA (Ed25519) and its header names the key with kid. The public keys are published, unauthenticated, at:
https://orc8r.com/.well-known/orc-access/jwks.json
It is an ordinary JWKS, cacheable for five minutes:
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"use": "sig",
"alg": "EdDSA",
"kid": "9cfbac5a7ac647aa",
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
}
]
}
Fetch it once, cache it, and refetch when you see a kid you do not know.
The claims
{
"iss": "https://orc8r.com",
"aud": "shop--obs.acme.example.com",
"sub": "u_d3g2k9x1m4p7",
"email": "ada@example.com",
"name": "Ada Lovelace",
"org": "acme",
"project": "shop",
"pool": "obs",
"endpoint": "api",
"iat": 1756800000,
"exp": 1756800300
}
Check three things, in this order, and reject the request if any of them fails:
- the signature, against the key whose
kidthe token names; aud— it must equal your own access host, the one inX-Forwarded-Host. This is what stops an assertion minted for another service being replayed at yours;iss— it must be your control plane's URL,https://orc8r.com, andexpmust be in the future.
Everything else is description: sub is the same id X-Orc-User carries, org, project, pool and endpoint say which route you were reached as, and name is absent when the account has no display name.
An assertion is good for five minutes and one is reused for up to a minute per person per route, so iat is not a per-request timestamp — do not treat it as a nonce, and do not use the assertion as a replay-proof action token.
With curl and jq
Read the keys:
curl -s https://orc8r.com/.well-known/orc-access/jwks.json | jq .
And, from inside a service that received a request, look at what it was told (decoding is not verifying — this is for seeing the shape):
printf '%s' "$HTTP_X_ORC_ACCESS_ASSERTION" \
| cut -d. -f2 \
| tr '_-' '/+' \
| base64 -d 2>/dev/null \
| jq .
Every JWT library that supports EdDSA and JWKS does the real check in a few lines: fetch the document, select by kid, verify, then compare aud and iss to your own configuration.
Rotation
The zone settings page has Rotate assertion key. Rotating mints a new key immediately and keeps publishing the previous public key for 24 hours, so a service holding a cached JWKS, or an assertion minted a moment before the rotation, keeps verifying. A library that refetches the document on an unknown kid needs no attention at all.
If no assertion header arrives at all, the server has nothing to sign with yet or does not know its own public URL — set Web URL on the zone settings page. Fall back to the identity headers, or refuse: your app decides which.
Related pages
- Project and organization exposure — the scopes a service can be reached at.
- Networking overview — names, and who can reach a service.
- API keys — the credential a script presents instead of signing in.